
在开发基于javascript的测验游戏时,一个常见的陷阱是由于事件监听器管理不当导致积分或计数重复增加。原始代码中,elegirrespuesta() 函数在每次调用 iterarjuego() 时都会为每个选项按钮重新添加 click 事件监听器。这意味着,当用户回答完第一道题并进入下一题时,新的监听器会被添加到旧的监听器之上。如果 iterarjuego() 被调用了 n 次,那么每个选项按钮上就会有 n 个 click 监听器。
当用户点击一个选项时,所有这些重复的监听器都会被触发,导致 funAnalizar() 函数被调用多次,进而 respCorrecta() 或 respIncorrecta() 函数也会被多次执行。例如,在第二道题时,每个点击会触发两次事件,使得积分和题目计数都增加两倍,从而出现积分重复计算的错误。
为了解决上述问题,我们可以采用更优雅且高效的事件处理方式:将所有选项包装在一个HTML <form> 元素中,并监听该表单的 submit 事件。这种方法有以下几个优点:
首先,我们需要调整HTML结构,将问题、选项和提交按钮放置在一个 <form> 元素内。使用 <input type="radio"> 元素来表示多选一的选项,并确保它们拥有相同的 name 属性,以便浏览器将它们视为一组,且只有一个可以被选中。
<html lang="en">
<head>
<title>JavaScript测验游戏</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
/* 样式代码,保持原样或根据需要调整 */
html { font: 300 3ch/1.2 'Segoe UI' }
#score { font-size: 1.25rem; }
#score::before { content: 'Score: ' }
ol { list-style: lower-latin; margin-top: 0 }
input, button { font: inherit }
li { margin-bottom: 8px; }
button { display: inline-flex; align-items: center; padding: 0 0.5rem; cursor: pointer }
</style>
</head>
<body>
<main>
<!-- 使用 <form> 元素包裹测验内容 -->
<form id='QA'>
<fieldset>
<legend><output id='score'></output></legend>
<output id='question'></output>
<ol>
<li>
<input id='optA' name='opt' type='radio' value='optA'>
<label for='optA'></label>
</li>
<li>
<input id='optB' name='opt' type='radio' value='optB'>
<label for='optB'></label>
</li>
<li>
<input id='optC' name='opt' type='radio' value='optC'>
<label for='optC'></label>
</li>
<li>
<input id='optD' name='opt' type='radio' value='optD'>
<label for='optD'></label>
</li>
</ol>
<menu>
<!-- 提交按钮 -->
<button id='next'>Next</button>
</menu>
</fieldset>
</form>
</main>
<script>
// JavaScript 代码将在这里
</script>
</body>
</html>关键点:
立即学习“Java免费学习笔记(深入)”;
在JavaScript中,我们将利用 document.forms 和 form.elements 接口来简化对表单元素的访问。
document.forms 允许通过ID或name属性直接访问页面中的表单。HTMLFormElement.elements 属性则返回一个 HTMLFormControlsCollection,其中包含了表单中的所有控件。
// 获取表单元素
const QA = document.forms.QA;
// 获取表单内的所有控件
const IO = QA.elements;
// 直接通过name或id访问控件
const question = IO.question; // <output id='question'>
const score = IO.score; // <output id='score'>
// 选项的label元素,用于显示文本
const opt1 = IO.optA.nextElementSibling;
const opt2 = IO.optB.nextElementSibling;
const opt3 = IO.optC.nextElementSibling;
const opt4 = IO.optD.nextElementSibling;测验题目数据可以保持类似的数组对象结构,但为了更好地与HTML中的 value 属性对应,可以将正确答案存储为选项的 value (例如 'optA')。
let qID = 0; // 当前题目索引
let totalP = 0; // 总得分
let totalQ = 0; // 总题目数
// 测验题目数组
const qArray = [{
qID: 0,
ques: "Que significa AI en Japonés?",
optA: 'amor',
optB: 'carcel',
optC: 'pizza',
optD: 'caja',
right: 'optA' // 正确答案对应选项的value
}, {
qID: 1,
ques: "Cual es el hiragana 'ME' ?",
optA: 'ぬ',
optB: 'ね',
optC: 'ぐ',
optD: 'め',
right: 'optD'
}, {
qID: 2,
ques: "En hiragana: DESAYUNO , ALMUERZO , CENA ?",
optA: 'ぬ',
optB: 'ね',
optC: 'ぐ',
optD: 'め',
right: 'optB'
}, {
qID: 3,
ques: "Como se dice madre y padre ?",
optA: 'chichi hana',
optB: 'hana mitsu',
optC: 'kirei chichi',
optD: 'undo chichi',
right: 'optC'
}, {
qID: 4,
ques: "Que significa きれい ?",
optA: 'rey y reina',
optB: 'lindo y linda',
optC: 'hermoso y hermosa',
optD: 'salvaje y saldro',
right: 'optB'
}];a. quiz() 函数:加载题目
这个函数负责根据当前 qID 更新页面上的问题和选项文本,并清除之前选中的电台按钮。
function quiz() {
// 更新问题文本
question.textContent = (qID + 1) + '. ' + qArray[qID].ques;
// 更新选项文本
opt1.textContent = qArray[qID].optA;
opt2.textContent = qArray[qID].optB;
opt3.textContent = qArray[qID].optC;
opt4.textContent = qArray[qID].optD;
// 遍历所有名为 'opt' 的radio按钮,取消选中状态
[...IO.opt].forEach(o => {
if (o.checked) {
o.checked = false;
}
});
}b. evaluate() 函数:处理表单提交
这是核心的事件处理函数,它绑定到表单的 submit 事件。
// 绑定表单的onsubmit事件
QA.onsubmit = evaluate;
function evaluate(e) {
// 阻止表单的默认提交行为,防止页面刷新
e.preventDefault();
// 获取被选中的radio按钮的value
let selected = IO.opt.value;
// 判断答案是否正确
if (selected === qArray[qID].right) {
correct();
} else {
wrong();
}
}注意事项:
c. correct() 和 wrong() 函数:更新分数和题目
这两个函数负责更新分数、题目计数,并加载下一道题。
function correct() {
totalP++; // 积分增加
totalQ++; // 题目数增加
score.textContent = totalP + " / " + totalQ; // 更新显示
qID++; // 移动到下一题
// 如果所有题目都已回答,隐藏“Next”按钮
if (qID >= qArray.length) {
return IO.next.style.display = 'none';
}
quiz(); // 加载下一题
}
function wrong() {
totalQ++; // 题目数增加(不加分)
score.textContent = totalP + " / " + totalQ; // 更新显示
qID++; // 移动到下一题
// 如果所有题目都已回答,隐藏“Next”按钮
if (qID >= qArray.length) {
return IO.next.style.display = 'none';
}
quiz(); // 加载下一题
}最后,在页面加载完成后,调用 quiz() 函数来显示第一道题。
// 页面加载后启动测验
quiz();将上述HTML和JavaScript代码整合后,得到一个功能完善且避免了重复计分问题的测验游戏。
<html lang="en">
<head>
<title>JavaScript测验游戏</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<style>
html { font: 300 3ch/1.2 'Segoe UI' }
#score { font-size: 1.25rem; }
#score::before { content: 'Score: ' }
ol { list-style: lower-latin; margin-top: 0 }
input, button { font: inherit }
li { margin-bottom: 8px; }
button { display: inline-flex; align-items: center; padding: 0 0.5rem; cursor: pointer }
</style>
</head>
<body>
<main>
<form id='QA'>
<fieldset>
<legend><output id='score'></output></legend>
<output id='question'></output>
<ol>
<li>
<input id='optA' name='opt' type='radio' value='optA'>
<label for='optA'></label>
</li>
<li>
<input id='optB' name='opt' type='radio' value='optB'>
<label for='optB'></label>
</li>
<li>
<input id='optC' name='opt' type='radio' value='optC'>
<label for='optC'></label>
</li>
<li>
<input id='optD' name='opt' type='radio' value='optD'>
<label for='optD'></label>
</li>
</ol>
<menu>
<button id='next'>Next</button>
</menu>
</fieldset>
</form>
</main>
<script>
const QA = document.forms.QA;
const IO = QA.elements;
const question = IO.question;
const score = IO.score;
const opt1 = IO.optA.nextElementSibling;
const opt2 = IO.optB.nextElementSibling;
const opt3 = IO.optC.nextElementSibling;
const opt4 = IO.optD.nextElementSibling;
let qID = 0;
let totalP = 0;
let totalQ = 0;
function quiz() {
question.textContent = (qID + 1) + '. ' + qArray[qID].ques;
opt1.textContent = qArray[qID].optA;
opt2.textContent = qArray[qID].optB;
opt3.textContent = qArray[qID].optC;
opt4.textContent = qArray[qID].optD;
[...IO.opt].forEach(o => {
if (o.checked) {
o.checked = false;
}
});
}
QA.onsubmit = evaluate;
function evaluate(e) {
e.preventDefault();
let selected = IO.opt.value;
if (selected === qArray[qID].right) {
correct();
} else {
wrong();
}
}
function correct() {
totalP++;
totalQ++;
score.textContent = totalP + " / " + totalQ;
qID++;
if (qID >= qArray.length) {
return IO.next.style.display = 'none';
}
quiz();
}
function wrong() {
totalQ++;
score.textContent = totalP + " / " + totalQ;
qID++;
if (qID >= qArray.length) {
return IO.next.style.display = 'none';
}
quiz();
}
const qArray = [{
qID: 0,
ques: "Que significa AI en Japonés?",
optA: 'amor',
optB: 'carcel',
optC: 'pizza',
optD: 'caja',
right: 'optA'
}, {
qID: 1,
ques: "Cual es el hiragana 'ME' ?",
optA: 'ぬ',
optB: 'ね',
optC: 'ぐ',
optD: 'め',
right: 'optD'
}, {
qID: 2,
ques: "En hiragana: DESAYUNO , ALMUERZO , CENA ?",
optA: 'ぬ',
optB: 'ね',
optC: 'ぐ',
optD: 'め',
right: 'optB'
}, {
qID: 3,
ques: "Como se dice madre y padre ?",
optA: 'chichi hana',
optB: 'hana mitsu',
optC: 'kirei chichi',
optD: 'undo chichi',
right: 'optC'
}, {
qID: 4,
ques: "Que significa きれい ?",
optA: 'rey y reina',
optB: 'lindo y linda',
optC: 'hermoso y hermosa',
optD: 'salvaje y saldro',
right: 'optB'
}];
quiz();
</script>
</body>
</html>通过将测验逻辑从多个 click 事件监听器重构为单个 form 的 submit 事件监听器,我们成功解决了JavaScript测验中积分重复计算的问题。这种方法不仅代码更简洁、更易于维护,而且也遵循了Web开发的最佳实践:
掌握这些技巧将有助于开发更健壮、性能更优的Web应用程序。
以上就是JavaScript测验游戏积分重复计算问题的解决方案与优化实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号