JavaScript Tutorial
JavaScript Project: Quiz
A scored multiple-choice quiz stored as an array of questions. Show the score at the end.
What you will build
A four-question quiz. Each item is a prompt, three choices, and the index of the right answer. The page shows one question at a time. A click on a choice records the score and advances. After the last item, the page shows how many were correct.
The questions live in an array, not in duplicated HTML blocks. Adding a fifth question is another object in the list. The render function already knows how to draw it.
Open an example with Try it in JavaScript. That loads /javascript/try — a live page and a console.
Methods you will use
| Method | Job on this page |
|---|---|
| array of questions | Stores prompt, choices, and the correct index |
click | Selects an answer and moves to the next item |
| score | Counts how many choices matched answer |
forEach | Builds one button per choice |
textContent | Writes the prompt, the progress, and the final score |
Store the correct answer as an index, not as a string. Then a later wording change does not break the check. Compare choiceIndex === question.answer.
Build in slices
Shell markup: a progress line, a prompt, a box for choice buttons, and a result line that stays empty until the end.
Example
<p id="progress">Question 1 of 4</p>
<h2 id="prompt"></h2>
<div id="choices"></div>
<p id="result"></p>The data is an array of objects. Keep the copy short. Three choices each. answer is 0, 1, or 2.
Example
const questions = [
{
prompt: "Which method writes text into an element?",
choices: ["textContent", "prompt()", "location.reload()"],
answer: 0
},
{
prompt: "What does addEventListener(\"click\") wait for?",
choices: ["A timer", "A mouse click", "A network response"],
answer: 1
},
{
prompt: "Which value is an array?",
choices: ["{ a: 1 }", "[1, 2, 3]", "\"1, 2, 3\""],
answer: 1
},
{
prompt: "What does array.length report?",
choices: ["The last index", "The number of items", "The first item"],
answer: 1
}
];Render the current index. Each button remembers its index in a closure. After a click, either score and advance, or show the final line and hide the buttons.
Example
<p id="progress"></p>
<h2 id="prompt"></h2>
<div id="choices"></div>
<p id="result"></p>
<script>
const questions = [
{ prompt: "Which method writes text into an element?", choices: ["textContent", "prompt()", "alert()"], answer: 0 },
{ prompt: "What does array.length report?", choices: ["The last index", "The number of items", "The first item"], answer: 1 }
];
let index = 0;
let score = 0;
const promptEl = document.getElementById("prompt");
const choicesEl = document.getElementById("choices");
const progressEl = document.getElementById("progress");
const resultEl = document.getElementById("result");
function showQuestion() {
const item = questions[index];
progressEl.textContent = "Question " + (index + 1) + " of " + questions.length;
promptEl.textContent = item.prompt;
resultEl.textContent = "";
choicesEl.replaceChildren();
item.choices.forEach(function (label, choiceIndex) {
const button = document.createElement("button");
button.type = "button";
button.textContent = label;
button.addEventListener("click", function () {
if (choiceIndex === item.answer) score += 1;
index += 1;
if (index < questions.length) showQuestion();
else showScore();
});
choicesEl.appendChild(button);
});
}
function showScore() {
progressEl.textContent = "Done";
promptEl.textContent = "Quiz complete";
choicesEl.replaceChildren();
resultEl.textContent = "You scored " + score + " out of " + questions.length + ".";
}
showQuestion();
</script>Run this at /javascript/try with Try it in JavaScript. Click every first choice once to confirm the score is not always four.
Complete document
This file is the quiz you would keep. Choice buttons stack as a column. A Restart button returns index and score to zero without reloading. The questions stay in one array at the top of the script.
Example
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quiz</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
background: #fefce8;
font-family: "Segoe UI", system-ui, sans-serif;
color: #1c1917;
}
.card {
width: min(26rem, 92vw);
background: #fffbeb;
border: 1px solid #fde68a;
padding: 1.4rem 1.5rem 1.5rem;
}
h1 { margin: 0 0 0.35rem; font-size: 1.4rem; }
#progress { margin: 0 0 0.85rem; color: #78716c; }
#prompt { margin: 0 0 0.9rem; font-size: 1.15rem; }
#choices { display: grid; gap: 0.45rem; }
button {
text-align: left;
padding: 0.55rem 0.7rem;
border: 1px solid #fde68a;
background: #fff;
font: inherit;
cursor: pointer;
}
button:hover { background: #fef9c3; }
#restart {
margin-top: 0.9rem;
background: #854d0e;
color: #fffbeb;
border: 0;
font-weight: 700;
display: none;
}
#result { margin: 0.85rem 0 0; font-weight: 700; }
</style>
</head>
<body>
<article class="card">
<h1>JavaScript Quiz</h1>
<p id="progress"></p>
<h2 id="prompt"></h2>
<div id="choices"></div>
<p id="result"></p>
<button id="restart" type="button">Restart</button>
</article>
<script>
const questions = [
{
prompt: "Which method writes text into an element?",
choices: ["textContent", "prompt()", "location.reload()"],
answer: 0
},
{
prompt: "What does addEventListener(\"click\") wait for?",
choices: ["A timer", "A mouse click", "A network response"],
answer: 1
},
{
prompt: "Which value is an array?",
choices: ["{ a: 1 }", "[1, 2, 3]", "\"1, 2, 3\""],
answer: 1
},
{
prompt: "What does array.length report?",
choices: ["The last index", "The number of items", "The first item"],
answer: 1
}
];
let index = 0;
let score = 0;
const promptEl = document.getElementById("prompt");
const choicesEl = document.getElementById("choices");
const progressEl = document.getElementById("progress");
const resultEl = document.getElementById("result");
const restartBtn = document.getElementById("restart");
function showQuestion() {
const item = questions[index];
progressEl.textContent = "Question " + (index + 1) + " of " + questions.length;
promptEl.textContent = item.prompt;
resultEl.textContent = "";
restartBtn.style.display = "none";
choicesEl.replaceChildren();
item.choices.forEach(function (label, choiceIndex) {
const button = document.createElement("button");
button.type = "button";
button.textContent = label;
button.addEventListener("click", function () {
if (choiceIndex === item.answer) score += 1;
index += 1;
if (index < questions.length) showQuestion();
else showScore();
});
choicesEl.appendChild(button);
});
}
function showScore() {
progressEl.textContent = "Done";
promptEl.textContent = "Quiz complete";
choicesEl.replaceChildren();
resultEl.textContent = "You scored " + score + " out of " + questions.length + ".";
restartBtn.style.display = "inline-block";
}
restartBtn.addEventListener("click", function () {
index = 0;
score = 0;
showQuestion();
});
showQuestion();
</script>
</body>
</html>One question at a time
Clearing the choice box with replaceChildren removes the old buttons and their listeners. That is simpler than disabling every button after a click. The next question gets a fresh set.
Do not show the correct answer in the result line for this project. The job is a score, not a review sheet. A practice task can add a missed-question list if you want that later.
Common mistakes
- Comparing the button label to the answer string. Two choices that share a word would collide. Use the index.
- Incrementing
indexbefore readingquestions[index]. You would skip the first item. - Leaving old buttons in the box. Clicks would fire on a previous question and double-count.
- Using
innerHTMLto inject choice text. A quote in the prompt would break the markup. UsetextContent. - Hard-coding "Question 1 of 4" in HTML and never updating it. Read
questions.length.
Practice tasks
- Add a fifth question about
localStorage. Keep the same object shape. - After each click, briefly show Correct or Miss before advancing. Use
setTimeoutfor 700ms. - List the missed prompts under the score. Preview at /javascript/try.