簡體   English   中英

使用 javascript 測驗應用程序並在動態生成問題/答案時遇到問題

[英]working on a javascript quiz app and having an issue dynamically generating questions/answers

我正在嘗試在問答游戲中動態生成問題和答案。 問題/答案保存在一組對象中,並且應該在 div 內一個接一個地生成。 當用戶點擊一個答案時,就會產生下一個問題。 現在有5個問題,問題是生成前兩個問題后,跳過第3個,生成第4個,然后跳過第5個。 當我提交第二個問題的答案時,控制台中會顯示第二個和第三個問題已得到回答,即使第三個問題從未出現在頁面上。

這是javascript-

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;



//function to start the game//
const startGame = () => {

    $('.start-btn').hide()
    $('.game-header').hide()
    $('.container').show();

    renderQuestion()

}



const renderQuestion = () => {

    $('#question').text(questions[questionIndex].question);
    $('#answer1').text(questions[questionIndex].choices[0])
    $('#answer2').text(questions[questionIndex].choices[1])
    $('#answer3').text(questions[questionIndex].choices[2])
    $('#answer4').text(questions[questionIndex].choices[3])



    $('.btn').click(function () {
        let response = $(this).text()
        console.log(response)
        checkAnswer(response)
    })


}





//function to set the timer


//function to end the quiz


//function to save high score




const checkAnswer = (response) => {


    if (response === questions[questionIndex].answer) {
        window.alert('CORRECT!')
    } else {
        window.alert('INCORRECT!')
    }

    

    questionIndex++
    console.log(questionIndex)

    renderQuestion()

}




$('.start-btn').click(startGame)

我相信問題與我在問題數組之后聲明的“questionIndex”變量有關。 在 checkAnswer 函數的底部,我有 questionIndex++,它應該循環遍歷所有問題,但我不確定我做錯了什么。

這是html-

<body>
    <div class="timer">
        Time left
    </div>
    <div class="game-header">
        <h1>Welcome to the WWII trivia game!</h1>
        <h2>Answer all the questions before the time runs out and check your score in the end!</h2>
    </div>
    <button class="start-btn">Start!</button>
    <div class="container">
        <div id="question-container" class="hide">
            <div id="question">""</div>
            <div class="answers">
                <button id='answer1' class="btn">Answer 1</button>
                <button id='answer2' class="btn">Answer 2</button>
                <button id='answer3' class="btn">Answer 3</button>
                <button id='answer4' class="btn">Answer 4</button>
            </div>
        </div>
        <div class="controls">
            <button id="start-btn" class="start-btn btn">Start</button>
            <button id="start-btn" class="start-btn btn hide">Next</button>
        </div>

    </div>

    <script src="script.js"></script>

</body>

所以總結一下,我試圖循環遍歷數組中的所有問題和相應的答案,以便在問答游戲中一個接一個地生成它們。 截至目前,第 3 和第 5 題被省略。 感謝任何幫助:)

我認為問題之一是您沒有檢查您的 questionIndex 是否超出范圍(0-4)。 此外,當您從箭頭函數調用 checkAnswer() 函數時,您將該箭頭函數分配給具有 btn 類的每個元素。 因此,如果您單擊答案按鈕,然后單擊下一步按鈕,您會將 questionIndex 的值增加兩倍。 也許這就是問題所在。

這是我對您的問題的解決方案:

 const start = document.getElementById('start'); const a1 = document.getElementById('answer1'); const a2 = document.getElementById('answer2'); const a3 = document.getElementById('answer3'); const a4 = document.getElementById('answer4'); const q = document.getElementById('question'); a1.onclick = handleRes; a2.onclick = handleRes; a3.onclick = handleRes; a4.onclick = handleRes; 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; function handleRes(res) { console.log(res.path[0].innerHTML); checkAnswer(res.path[0].innerHTML); } //function to start the game// const startGame = () => { /*$('.start-btn').hide() $('.game-header').hide() $('.container').show();*/ renderQuestion() } const renderQuestion = () => { /*$('#question').text(questions[questionIndex].question); $('#answer1').text(questions[questionIndex].choices[0]) $('#answer2').text(questions[questionIndex].choices[1]) $('#answer3').text(questions[questionIndex].choices[2]) $('#answer4').text(questions[questionIndex].choices[3])*/ q.innerHTML = questions[questionIndex].question; a1.innerHTML = questions[questionIndex].choices[0]; a2.innerHTML = questions[questionIndex].choices[1]; a3.innerHTML = questions[questionIndex].choices[2]; a4.innerHTML = questions[questionIndex].choices[3]; } //function to set the timer //function to end the quiz //function to save high score const checkAnswer = (response) => { if (response === questions[questionIndex].answer) { window.alert('CORRECT!') } else { window.alert('INCORRECT!') } if (questionIndex == questions.length - 1) { return; } questionIndex++ console.log(questionIndex) renderQuestion() } start.addEventListener('click', startGame);
 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>Document</title> </head> <body> <div class="timer"> Time left </div> <div class="game-header"> <h1>Welcome to the WWII trivia game!</h1> <h2>Answer all the questions before the time runs out and check your score in the end!</h2> </div> <button id="start" class="start-btn">Start!</button> <div class="container"> <div id="question-container" class="hide"> <div id="question">""</div> <div class="answers"> <button id='answer1' onclick="handleRes(this.innerHTML)" class="btn">Answer 1</button> <button id='answer2' onclick="handleRes(this.innerHTML)" class="btn">Answer 2</button> <button id='answer3' onclick="handleRes(this.innerHTML)" class="btn">Answer 3</button> <button id='answer4' onclick="handleRes(this.innerHTML)" class="btn">Answer 4</button> </div> </div> <div class="controls"> <button id="start-btn" class="start-btn btn">Start</button> <button id="start-btn" class="start-btn btn hide">Next</button> </div> </div> <script src="script.js"></script> </body> </html>

正如評論中提到的,您應該將需要執行的不同任務分離到它們自己的功能中。 除了其他好處之外,您可以更快地發現邏輯錯誤。 如果每次有人嘗試回答時都增加 questionIndex,那么當他們回答錯誤時就會遇到問題。 他們需要多次點擊按鈕,這將多次更改 questionIndex。

這是一個在沒有 jQuery 的頁面上工作的版本。

 "use strict"; function byId(id){return document.getElementById(id)} function qsa(sel,par=document){return par.querySelectorAll(sel)} function qs(sel,par=document){return par.querySelector(sel)} var questionIndex; window.addEventListener('load', onLoaded, false); function onLoaded(evt) { let ansBtns = qsa('.answers > .btn'); ansBtns.forEach( btn => btn.addEventListener('click', onAnswerBtnClicked, false) ); startGame(); } function startGame() { qs('.start-btn').style.display = 'none'; qs('.game-header').style.display = 'none'; qs('.container').style.display = ''; questionIndex = 0; renderQuestion(questionIndex); } function renderQuestion(index) { let curQ = questions[index]; qs('#question').textContent = curQ.question; qs('#answer1').textContent = curQ.choices[0]; qs('#answer2').textContent = curQ.choices[1]; qs('#answer3').textContent = curQ.choices[2]; qs('#answer4').textContent = curQ.choices[3]; } function isAnswerCorrect(answer, questionIndex) { let curQ = questions[questionIndex]; if (answer == curQ.answer) return true; else return false; } function onAnswerBtnClicked(evt) { let answer = this.textContent; if (isAnswerCorrect(answer, questionIndex)) { alert('correct'); if (questionIndex < 3) { questionIndex++; renderQuestion(questionIndex); } // all questions dealt with. // quiz finished. else { } } else alert('incorrect'); } 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" } ];
 <body> <div class="timer"> Time left </div> <div class="game-header"> <h1>Welcome to the WWII trivia game!</h1> <h2>Answer all the questions before the time runs out and check your score in the end!</h2> </div> <button class="start-btn">Start!</button> <div class="container"> <div id="question-container" class="hide"> <div id="question">""</div> <div class="answers"> <button id='answer1' class="btn">Answer 1</button> <button id='answer2' class="btn">Answer 2</button> <button id='answer3' class="btn">Answer 3</button> <button id='answer4' class="btn">Answer 4</button> </div> </div> <div class="controls"> <button id="start-btn" class="start-btn btn">Start</button> <button id="start-btn" class="start-btn btn hide">Next</button> </div> </div> </body>

問題

乍一看,OP 看起來不錯,因為沒有最小的可復制示例,所以我並沒有花時間去測試它。 我重新格式化了它,因為有大量的空行並且阻礙了調試——你需要看到盡可能多的代碼,你可以整齊地放在視線范圍內。 我還將唯一的頂級事件處理程序設置為頂部,它不會影響功能。 這是我在 OP 中看到的內容的細分:

/*
There are 7 buttons.
button#start.start-btn
button#answer1.btn
button#answer2.btn
button#answer3.btn
button#answer4.btn
button#start-btn.start-btn.btn <= Invalid id must be unique
button#start-btn.start-btn.btn.hide <= Invalid as above
- Although invalid they actual cause no problems
*/
/*
A. The first time button is clicked (button#start.start-btn):
  1. startGame() is called
  2. renderGame() is called
    a. ALL .btn's have an event handler that calls checkAnswer() 🪲
B. The second time a button is clicked (button#answerX.btn):
  1. checkAnswer() is called 💣
  2. questionIndex = 1 💣
  3. renderGame() is called 💣
    a. ALL .btn's have an event handler that calls checkAnswer() 🪲
C. User clicks button#answerX.btn again:
  1. checkAnswer() is called twice 💥
  2. questionIndex = 3 💥
  3. renderGame() is called twice 💥
    a. ALL .btn's have an event handler that calls checkAnswer() 🪲
    b. ALL .btn's have an event handler that calls checkAnswer() 🪲

Event handlers don't get overwritten they will accumulate and each one will 
fire when a click event happens on a bound tag.
*/
$('.start-btn').click(startGame); 

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

const renderQuestion = () => {
  $('#question').text(questions[questionIndex].question);
  $('#answer1').text(questions[questionIndex].choices[0]);
  $('#answer2').text(questions[questionIndex].choices[1]);
  $('#answer3').text(questions[questionIndex].choices[2]);
  $('#answer4').text(questions[questionIndex].choices[3]);

  $('.btn').click(function () { // 🪲
    let response = $(this).text();
    console.log(response);
    checkAnswer(response);
  });
};

const checkAnswer = (response) => {
  if (response === questions[questionIndex].answer) {
    window.alert('CORRECT!'); /* 💩 Even for testing alert() IMO is a code 
smell */
  } else {
    window.alert('INCORRECT!'); /* 💩 That goes for prompt() and confirm()
 as well */
  }
  questionIndex++;
  console.log(questionIndex);
  renderQuestion();
};

我不會向您展示如何修復該代碼,因為當其他內容發生更改時,它還會出現其他問題。 所以這是一個帶有評論的工作示例。 順便說一句,第四個 QA 有 5 個選擇——所以我刪除了“市場花園”。 最后我省略了 timer() 和詳細信息部分,我已經超出了問題的范圍。

細節在例子中注釋

 const qa = [{ prompt: "Where was the first attempted allied invasion of France?", choices: ["Normandy", "Nice", "Dieppe", "Bourdeaux"], correct: "Dieppe" }, { prompt: "Which American general was in charge of the pacific campaign?", choices: ["George Patton", "Omar Bradley", "George Marshall", "Douglas MacArthur"], correct: "Douglas MacArthur" }, { prompt: "When was V-Day?", choices: ["April", "May", "June", "July"], correct: "May" }, { prompt: "Which of these was considered the largest tank battle in history?", choices: ["Battle of the Bulge", "D-Day", "Kursk", "Stalingrad"], correct: "Kursk" }, { prompt: "When did WWII start?", choices: ["1939", "1940", "1941", "1944"], correct: "1939" }]; let q = 0; let userPicks = []; /* button.start clicked */ /* Rearrange interface start first QA with nextQA() button.next starts disabled */ $('.start').on('click', function() { //timer(duration); $('main').show(); $('header').hide(); $(this).hide(); $('.next').show().attr('disabled', true); nextQA(); }); /* Any input[type='radio'] is changed */ /* button.next is enabled This behavior forces user to answer the current QA to move on to the next QA */ $(':radio').on('change', function() { $('.next').prop('disabled', false); }); /* button.next is clicked */ /* Get the checked :radio and put it's value and the text of the label that sits next to it. ex. userPicks = [['0', 'Nice'], ['1', 'Douglas MacArthur']] That's the first two QA first is wrong, second is right */ /* Increment q. After the last QA this function gets "short circuited to invoke endQA(). Otherwise the :radio:checked gets unchecked and button.next gets disabled, then nextQA() is called */ $('.next').on('click', function() { userPicks.push([$(':radio:checked').val(), $(':radio:checked').next().text()]); ++q; if (q === qa.length) { return endQA(); } $(':radio:checked').prop('checked', false); $(this).prop('disabled', true); nextQA(); }); /* Populate output.question and each .pick p with current data from qa Change the value of the :radio with qa['correct'] data from '0' to '1' */ function nextQA() { $(".question").val(qa[q].prompt).attr('data-count', q + 1); $('.pick p').each(function(idx) { $(this).text(qa[q].choices[idx]); if ($(this).text() === qa[q].correct) { $(this).prev().val('1'); } else { $(this).prev().val('0'); } }); } /* Rearrange interface and sort out wrong answers and right answers. Display the score on output.score */ function endQA() { // timer(false); $('.QA').hide(); $('.next').hide(); $('.results').show(); $('.repeat').show(); let wrong = [], right = []; userPicks.forEach((ans, idx) => { if (ans[0] === '1') { right.push([idx, ans[1]]); } else { wrong.push([idx, ans[1]]); } }); $('.score').val(`${right.length} out of ${qa.length}`); }
 *, *::before, *::after { margin: 0; padding: 0 } html { font: 300 2ch/1.2 'Segoe UI' } body { padding: 8px; } header { margin-bottom: 10px; } main { display: none; } fieldset { padding: 0 1rem 1rem; } legend { margin-bottom: 8px; font-size: 1.5rem; } time { font-family: consolas; font-size: 1.75rem; } time::before { content: attr(datetime) } output { display: block; margin-bottom: 8px; } .question::before { content: attr(data-count)'. ' } ol { margin-left: 30px; } .qaList { list-style: lower-latin; } li { display: list-item; margin-bottom: 8px; } input, button { display: inline-block; font: inherit } .pick { display: inline-block; vertical-align: middle } .pick input { vertical-align: middle; } .pick p { display: inline-block; margin-left: 5px; } .pick:last-of-type p { vertical-align: text-top } .pick:last-of-type input { margin-top: -4.5px } .start, .next, .repeat { width: 100%; padding: 0.25rem 0; font-size: 1.5rem; font-weight: 500; cursor: pointer } .next, .repeat { display: none } summary { cursor: pointer }
 <header> <h1>WWII Trivia</h1> <p>Answer all the questions before the time runs out and check your score in the end</p> </header> <main> <form id='quiz'> <fieldset class='QA'> <legend> <time datetime="00:00"></time> </legend> <output class="question"></output> <ol class='qaList'> <li><label class='pick'> <input name='pick' type='radio' value='0'><p></p> </label></li> <li><label class='pick'> <input name='pick' type='radio' value='0'><p></p> </label></li> <li><label class='pick'> <input name='pick' type='radio' value='0'><p></p> </label></li> <li><label class='pick'> <input name='pick' type='radio' value='0'><p></p> </label></li> </ol> </fieldset> <fieldset class='results' hidden> <legend>Quiz Results</legend> <output class='score'></output> <details> <summary>Review Answers</summary> <ol class='endList'></ol> </details> </fieldset> </form> </main> <menu> <button class='start' type='button'>Start</button> <button class='next' type='button'>Next</button> <button class='repeat' type='button'>Start Again</button> </menu> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="script.js"></script>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM