实现Trivia答题网页时出现索引翻倍而非递增1的问题排查
Trivia答题网页索引翻倍问题解决
问题描述
我要开发一个包含10道题的Trivia答题网页,从JSON文件读取题目数组,展示题目与选项,点击选项判断对错后计分并进入下一题。目前遇到的问题是索引(index)翻倍,只能展示第1、2、4、8题;尝试多种索引递增方式都无效,移除finishCheck中的loop()后索引可正常递增,添加额外事件监听也没效果。
相关代码
finishCheck() { this.index += 1; if (this.index < this.questions.length) { this.showQuestion(); this.loop(); } else { this.showScore(); this.hideAnswers(); } } loop() { const h = document.querySelectorAll(".btn-primary"); h.forEach((o, i) => { o.innerHTML = this.questions[this.index].options[i].option; console.log(this.questions[this.index].options[i].option); o.addEventListener("click", () => { if (this.questions[this.index].options[i].isCorrect) { this.score++; } this.finishCheck(); }); }); }
问题原因
核心问题是重复绑定点击事件。每次调用loop()时,都会给所有.btn-primary按钮重新绑定一次click事件:
- 第一次点击按钮,触发1次
finishCheck(),index+1; - 第二次调用
loop()时,又给按钮绑定了新的click事件,此时按钮上已有2个事件监听; - 第二次点击时会触发2次
finishCheck(),index直接+2; - 第三次点击时按钮上已有4个事件监听,
index+4,以此类推,就出现了1→2→4→8的跳跃。
解决方案
方案1:绑定新事件前移除旧事件
使用命名函数代替匿名函数,这样可以在绑定新事件前移除旧的事件监听:
finishCheck() { this.index += 1; if (this.index < this.questions.length) { this.showQuestion(); this.loop(); } else { this.showScore(); this.hideAnswers(); } } handleOptionClick(i) { if (this.questions[this.index].options[i].isCorrect) { this.score++; } this.finishCheck(); } loop() { const buttons = document.querySelectorAll(".btn-primary"); buttons.forEach((btn, i) => { btn.innerHTML = this.questions[this.index].options[i].option; // 先移除旧事件,再绑定新事件 btn.removeEventListener("click", this.handleOptionClick.bind(this, i)); btn.addEventListener("click", this.handleOptionClick.bind(this, i)); }); }
方案2:仅绑定一次事件,动态更新选项
初始化时给按钮绑定一次事件,之后切换题目时只更新按钮文本,避免重复绑定:
constructor() { this.index = 0; this.score = 0; // 初始化时只绑定一次事件 this.bindEventListeners(); this.showQuestion(); this.updateOptions(); } bindEventListeners() { const buttons = document.querySelectorAll(".btn-primary"); buttons.forEach((btn, i) => { btn.addEventListener("click", () => { if (this.questions[this.index].options[i].isCorrect) { this.score++; } this.finishCheck(); }); }); } finishCheck() { this.index += 1; if (this.index < this.questions.length) { this.showQuestion(); this.updateOptions(); // 只更新选项文本,不重新绑定事件 } else { this.showScore(); this.hideAnswers(); } } updateOptions() { const buttons = document.querySelectorAll(".btn-primary"); buttons.forEach((btn, i) => { btn.innerHTML = this.questions[this.index].options[i].option; }); }
方案3:使用事件委托
把事件绑定到按钮的父元素上,通过事件冒泡处理点击,只需要一次绑定:
constructor() { this.index = 0; this.score = 0; // 绑定事件到选项容器父元素 document.querySelector(".options-container").addEventListener("click", (e) => { if (e.target.classList.contains("btn-primary")) { const buttonIndex = Array.from(document.querySelectorAll(".btn-primary")).indexOf(e.target); if (this.questions[this.index].options[buttonIndex].isCorrect) { this.score++; } this.finishCheck(); } }); this.showQuestion(); this.updateOptions(); } finishCheck() { this.index += 1; if (this.index < this.questions.length) { this.showQuestion(); this.updateOptions(); } else { this.showScore(); this.hideAnswers(); } } updateOptions() { const buttons = document.querySelectorAll(".btn-primary"); buttons.forEach((btn, i) => { btn.innerHTML = this.questions[this.index].options[i].option; }); }
内容的提问来源于stack exchange,提问作者Jamie McGrath
相关产品推荐
相关产品推荐

