You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript测验应用动态生成题目出现跳题漏题问题求解

问题根源

跳题是重复绑定点击事件导致的:

  • 你在renderQuestion函数中每次渲染题目时,都会用$('.btn').click()给所有答案按钮追加新的点击监听器,jQuery的click方法不会覆盖旧的同类型事件,只会持续累加绑定的处理函数。
  • 执行流程完全复现你遇到的现象:
    1. 页面加载完成点击开始,渲染第1题(数组下标0),给按钮绑定1个点击事件
    2. 点击第1题答案,触发1次checkAnswer,下标自增到1,渲染第2题,此时给按钮追加第2个点击事件,每个按钮累计2个事件
    3. 点击第2题答案时会连续触发2次checkAnswer:第一次执行后下标到2,渲染第3题同时追加第3个事件;第二次执行时下标已经是2,判题后下标自增到3,直接渲染第4题,第3题刚渲染就被覆盖,用户看不到
    4. 点击第4题答案时,按钮已经累计4个点击事件,会连续触发4次处理函数,直接跳过第5题,还会因为下标超出数组长度报读取undefined的错误。
修复方案
  1. 绑定新事件前先解绑旧事件,避免一次点击触发多次处理逻辑;更稳妥的方式是每次渲染题目时动态生成选项按钮,从根源上避免重复绑定问题,同时适配不同数量的选项(你现有代码固定渲染4个选项,第4题的第5个选项Market Garden会被漏渲染)
  2. 增加边界判断:当题目下标等于题库总长度时,终止答题流程,不要继续渲染题目,避免数组越界报错
修复后完整JS代码
const questions = [
    {
        question: "Where was the first attempted allied invasion of France?",
        choices: ['Normandy', 'Nice', 'Dieppe', "Bourdeaux"],
        answer: "Dieppe"
    },
    {
        question: "Which American general was in charge of the pacific campaign?",
        choices: ["George Patton", "Omar Bradley", "George Marshall", "Douglas MacArthur"],
        answer: "Douglas MacArthur"
    },
    {
        question: "When was VE day?",
        choices: ["April", "May", "June", "July"],
        answer: "May"
    },
    {
        question: "Which of these was considered the largest tank battle in history?",
        choices: ["Battle of the Bulge", "D-Day", "Kursk", "Stalingrad", "Market Garden"],
        answer: "Kursk"
    },
    {
        question: "When did the war start?",
        choices: ["1939", "1938", "1941", "1944"],
        answer: "1939"
    }
]

let questionIndex = 0;

// 开始游戏
const startGame = () => {
    $('.start-btn').hide()
    $('.game-header').hide()
    $('.container').show();
    renderQuestion()
}

// 渲染当前题目
const renderQuestion = () => {
    const currentQuestion = questions[questionIndex];
    // 渲染题干
    $('#question').text(currentQuestion.question);
    const answerWrap = $('.answers');
    // 清空旧选项
    answerWrap.empty();
    // 动态生成所有选项按钮
    currentQuestion.choices.forEach(choice => {
        const choiceBtn = $(`<button class="btn">${choice}</button>`);
        // 绑定点击事件,因为每次都是新生成的按钮,不会有重复绑定问题
        choiceBtn.click(function() {
            checkAnswer($(this).text());
        })
        answerWrap.append(choiceBtn);
    })
}

// 校验答案
const checkAnswer = (response) => {
    if (response === questions[questionIndex].answer) {
        window.alert('CORRECT!')
    } else {
        window.alert('INCORRECT!')
    }

    questionIndex++
    // 所有题目答完,执行结束逻辑
    if (questionIndex >= questions.length) {
        alert('Quiz completed!');
        // 这里可以替换成你自己的算分、展示高分榜逻辑
        return;
    }
    // 渲染下一题
    renderQuestion()
}

$('.start-btn').click(startGame)
最小改动方案

如果你不想改成动态生成选项的写法,只需要在原有renderQuestion的事件绑定前加解绑语句即可,修改后的事件绑定部分代码如下:

// 先解绑所有旧的click事件,再绑定新事件
$('.btn').off('click').click(function () {
    let response = $(this).text()
    checkAnswer(response)
})

注意这种写法仍然固定渲染4个选项,第4题的第5个选项不会显示,建议优先用动态生成选项的方案。


内容的提问来源于stack exchange,提问作者Ted

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 18:36:24