![](/img/trans.png)
[英]Generating checkboxes for answers to quiz questions with a loop over a constructor
[英]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.