The Story: Rahul’s Daily GK Practice
Rahul is preparing for a government job exam. Every exam has a general knowledge section, and the questions come fast: you read, choose and move on, with a clock running. Rahul wants a small practice tool on his phone that asks him questions one by one, shows if he was right, gives him only 15 seconds for each, and tells him his score at the end.
In this project you will build that quiz app with plain JavaScript. It is a great project because it has state (which question we are on, the score, the seconds left) and screens (the quiz screen and the result screen). Learning to manage these two ideas is the heart of building any interactive app.
How to follow this project: each step below is a complete page that you can run with ▶ Try it yourself. Each step adds one feature to the previous one, so compare the code to see what is new.
The Plan
| Step | Feature | Main skill |
|---|---|---|
| 1 | Layout and question data | Array of objects |
| 2 | Show one question | forEach, createElement |
| 3 | Click an option, see right or wrong | Event delegation, classes |
| 4 | Next button and progress | Moving through state |
| 5 | Score | Counters |
| 6 | 15-second timer | setInterval, clearInterval |
| 7 | Result screen and Play again | Percentages, resetting state |
Step 1: The Layout and the Questions
First, the look of the quiz, written by hand: a progress label, a score, the question and four option buttons. Under it, in JavaScript, we write our five questions as an array of objects.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<div class="top">
<span id="progress">Question 1 of 5</span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question">What is the capital of India?</p>
<div class="options" id="options">
<button type="button" class="option">Mumbai</button>
<button type="button" class="option">Kolkata</button>
<button type="button" class="option">New Delhi</button>
<button type="button" class="option">Chennai</button>
</div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
console.log(`Loaded ${questions.length} questions`); // Loaded 5 questions
console.log(questions[0].options[questions[0].answer]); // New Delhi
</script>
</body>
</html>
Each question object has three properties:
question: the text to show.options: an array of four answers.answer: the index (position) of the right option. Arrays count from 0, soanswer: 2means the third option, “New Delhi”.
The second console.log proves the data is right: questions[0].options[questions[0].answer] reads the first question’s correct option. Keeping questions as data, separate from the HTML, means Rahul can add a hundred questions later without touching the page.
Step 2: Show One Question from the Array
Now the HTML has empty containers, and JavaScript fills them. The variable current remembers which question we are on. It starts at 0.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<div class="top">
<span id="progress"></span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
let current = 0;
function showQuestion() {
const q = questions[current];
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
}
showQuestion();
</script>
</body>
</html>
const q = questions[current]picks the current question object.optionsEl.innerHTML = ''removes old buttons before drawing new ones.forEach((option, index) => ...)gives us each option and its position.btn.dataset.index = indexstores the position on the button as adata-indexattribute, so we know later which option was clicked.
Change let current = 0 to 3 and run it: the planet question appears. The page is now driven by data.
Step 3: Click an Option and Highlight Right or Wrong
When Rahul taps an option, the right answer should turn green. If he chose wrongly, his choice should turn red. After that, no more clicks should count.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<div class="top">
<span id="progress"></span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
let current = 0;
function showQuestion() {
const q = questions[current];
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
}
function revealAnswer(chosenIndex) {
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
showQuestion();
</script>
</body>
</html>
- We use event delegation: one listener on the options container, not one per button. The buttons are rebuilt for every question, but the container stays.
closest('.option')finds the button that was clicked. revealAnswer(chosenIndex)loops over every button withfor...ofonoptionsEl.children.Number(btn.dataset.index)turns the attribute back into a number, because attributes are always strings.- Every button gets
disabled = true, so the answer is locked. - The correct button gets the
correctclass. The chosen button, if it is a different one, getswrong. The CSS colours them.
Showing the right answer even when Rahul is wrong is good teaching: he learns something from every mistake.
Step 4: The Next Button and Progress
Now we can move through all five questions. The Next button stays disabled until Rahul answers, so he cannot skip a question by accident.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<div class="top">
<span id="progress"></span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
const progressEl = document.querySelector('#progress');
const nextBtn = document.querySelector('#next-btn');
let current = 0;
function showQuestion() {
const q = questions[current];
progressEl.textContent = `Question ${current + 1} of ${questions.length}`;
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
nextBtn.disabled = true;
nextBtn.textContent = current === questions.length - 1 ? 'See result' : 'Next';
}
function revealAnswer(chosenIndex) {
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
nextBtn.disabled = false;
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
nextBtn.addEventListener('click', () => {
current++;
if (current < questions.length) {
showQuestion();
} else {
progressEl.textContent = 'Finished';
questionEl.textContent = 'Quiz finished! Well done.';
optionsEl.innerHTML = '';
nextBtn.disabled = true;
}
});
showQuestion();
</script>
</body>
</html>
- In
showQuestion(), a template string writes “Question 2 of 5”. We add 1 becausecurrentstarts at 0 but people count from 1. - On the last question, the button text changes to “See result”. The ternary operator
condition ? a : bchooses between the two words. revealAnswernow ends withnextBtn.disabled = false.- The Next handler adds 1 to
current. If there are questions left, we show the next one. If not, we show a simple “finished” message for now.
Step 5: Keep the Score
A new variable, score, starts at 0. Inside revealAnswer, if the chosen index equals the correct index, we add 1 and update the score label.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<div class="top">
<span id="progress"></span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
const progressEl = document.querySelector('#progress');
const nextBtn = document.querySelector('#next-btn');
const scoreEl = document.querySelector('#score');
let current = 0;
let score = 0;
function showQuestion() {
const q = questions[current];
progressEl.textContent = `Question ${current + 1} of ${questions.length}`;
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
nextBtn.disabled = true;
nextBtn.textContent = current === questions.length - 1 ? 'See result' : 'Next';
}
function revealAnswer(chosenIndex) {
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
if (chosenIndex === correctIndex) {
score++;
scoreEl.textContent = `Score: ${score}`;
}
nextBtn.disabled = false;
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
nextBtn.addEventListener('click', () => {
current++;
if (current < questions.length) {
showQuestion();
} else {
progressEl.textContent = 'Finished';
questionEl.textContent = `Quiz finished! You scored ${score} out of ${questions.length}.`;
optionsEl.innerHTML = '';
nextBtn.disabled = true;
}
});
showQuestion();
</script>
</body>
</html>
Notice that the score logic lives in exactly one place. Later, when the timer runs out, it will call the same revealAnswer function with null. Since null never equals a real index, a timeout simply scores nothing, with no extra code.
Step 6: A 15-Second Timer for Each Question
Real exams are timed, so Rahul wants pressure. setInterval(fn, 1000) runs a function every 1000 milliseconds (one second) and returns an id. clearInterval(id) stops it. The JavaScript Event Loop lesson explains how timers work behind the scenes; here we use them for real.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<div class="top">
<span id="progress"></span>
<span id="timer">Time: 15s</span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
const progressEl = document.querySelector('#progress');
const nextBtn = document.querySelector('#next-btn');
const scoreEl = document.querySelector('#score');
const timerEl = document.querySelector('#timer');
const TIME_LIMIT = 15;
let current = 0;
let score = 0;
let timeLeft = TIME_LIMIT;
let timerId = null;
function showQuestion() {
const q = questions[current];
progressEl.textContent = `Question ${current + 1} of ${questions.length}`;
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
nextBtn.disabled = true;
nextBtn.textContent = current === questions.length - 1 ? 'See result' : 'Next';
startTimer();
}
function startTimer() {
clearInterval(timerId);
timeLeft = TIME_LIMIT;
timerEl.textContent = `Time: ${timeLeft}s`;
timerId = setInterval(() => {
timeLeft--;
timerEl.textContent = `Time: ${timeLeft}s`;
if (timeLeft === 0) {
timerEl.textContent = "Time's up!";
revealAnswer(null);
}
}, 1000);
}
function revealAnswer(chosenIndex) {
clearInterval(timerId);
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
if (chosenIndex === correctIndex) {
score++;
scoreEl.textContent = `Score: ${score}`;
}
nextBtn.disabled = false;
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
nextBtn.addEventListener('click', () => {
current++;
if (current < questions.length) {
showQuestion();
} else {
progressEl.textContent = 'Finished';
questionEl.textContent = `Quiz finished! You scored ${score} out of ${questions.length}.`;
optionsEl.innerHTML = '';
nextBtn.disabled = true;
}
});
showQuestion();
</script>
</body>
</html>
TIME_LIMITis a constant written in capitals, a common way to mark a fixed setting. Change it to 5 to test the timer quickly.timerIdstores the id fromsetInterval, so any function can stop the timer.startTimer()first callsclearInterval(timerId). This makes sure an old timer is never left running when a new question starts.- Every second,
timeLeft--takes away one and updates the label. At 0 we show “Time’s up!” and callrevealAnswer(null), which shows the right answer and unlocks Next. revealAnswernow starts withclearInterval(timerId), so answering stops the clock.
Why stop the timer? An interval keeps running until you clear it, even when the question has changed. Two timers running together would make the clock count down twice as fast. See Common Mistakes below.
Step 7: The Result Screen and Play Again
The final step turns the page into two screens. Both are <section> elements, and we show one at a time with the hidden property. This is the complete app.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.big { font-size: 2.5rem; font-weight: 700; color: #4f46e5; margin: 8px 0; }
[hidden] { display: none !important; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<section id="quiz-screen">
<div class="top">
<span id="progress"></span>
<span id="timer">Time: 15s</span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</section>
<section id="result-screen" hidden>
<p>Your result</p>
<p class="big" id="result-percent"></p>
<p id="result-text"></p>
<p id="result-message"></p>
<button type="button" class="main-btn" id="play-again">Play again</button>
</section>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
const progressEl = document.querySelector('#progress');
const nextBtn = document.querySelector('#next-btn');
const scoreEl = document.querySelector('#score');
const timerEl = document.querySelector('#timer');
const quizScreen = document.querySelector('#quiz-screen');
const resultScreen = document.querySelector('#result-screen');
const resultPercent = document.querySelector('#result-percent');
const resultText = document.querySelector('#result-text');
const resultMessage = document.querySelector('#result-message');
const playAgainBtn = document.querySelector('#play-again');
const TIME_LIMIT = 15;
let current = 0;
let score = 0;
let timeLeft = TIME_LIMIT;
let timerId = null;
function showQuestion() {
const q = questions[current];
progressEl.textContent = `Question ${current + 1} of ${questions.length}`;
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
nextBtn.disabled = true;
nextBtn.textContent = current === questions.length - 1 ? 'See result' : 'Next';
startTimer();
}
function startTimer() {
clearInterval(timerId);
timeLeft = TIME_LIMIT;
timerEl.textContent = `Time: ${timeLeft}s`;
timerId = setInterval(() => {
timeLeft--;
timerEl.textContent = `Time: ${timeLeft}s`;
if (timeLeft === 0) {
timerEl.textContent = "Time's up!";
revealAnswer(null);
}
}, 1000);
}
function revealAnswer(chosenIndex) {
clearInterval(timerId);
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
if (chosenIndex === correctIndex) {
score++;
scoreEl.textContent = `Score: ${score}`;
}
nextBtn.disabled = false;
}
function showResult() {
clearInterval(timerId);
const percent = Math.round((score / questions.length) * 100);
resultPercent.textContent = `${percent}%`;
resultText.textContent = `You answered ${score} out of ${questions.length} correctly.`;
if (percent >= 80) {
resultMessage.textContent = 'Excellent! You are exam ready.';
} else if (percent >= 50) {
resultMessage.textContent = 'Good effort. Revise once more and try again.';
} else {
resultMessage.textContent = 'Keep practising. Every attempt makes you better.';
}
quizScreen.hidden = true;
resultScreen.hidden = false;
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
nextBtn.addEventListener('click', () => {
current++;
if (current < questions.length) {
showQuestion();
} else {
showResult();
}
});
playAgainBtn.addEventListener('click', () => {
current = 0;
score = 0;
scoreEl.textContent = 'Score: 0';
resultScreen.hidden = true;
quizScreen.hidden = false;
showQuestion();
});
showQuestion();
</script>
</body>
</html>
Math.round((score / questions.length) * 100)turns, for example, 4 out of 5 into 80.Math.roundremoves decimals: with 3 questions, 2 right gives 66.67, which becomes 67.- An
if...else ifchain picks a friendly message for the percentage. quizScreen.hidden = truehides the quiz, andresultScreen.hidden = falseshows the result. The CSS rule[hidden] { display: none !important; }makes sure no other style can show a hidden screen by mistake.- Play again resets every piece of state:
current,scoreand the score label. Then it swaps the screens back and callsshowQuestion(), which starts a fresh timer.
Real-Life Example: Rahul’s Morning Practice
Every morning on the bus, Rahul opens the quiz. He gets the capital and the planet right, misses the rupee symbol year (it was adopted in 2010), and runs out of time on one question. The result screen shows 60% and tells him to revise once more. He presses Play again, and this time scores 100%. That is the full app from Step 7 doing its job.
Try these changes:
- Add a sixth question of your own to the array. The progress label and the percentage update by themselves.
- Set
TIME_LIMITto 10 for a harder quiz. - Change the messages on the result screen to suit your exam.
Ideas to Take It Further
- Shuffle the questions each time Rahul plays, so he cannot just remember the order.
- Review screen: keep an array of wrong answers and list them, with the correct option, on the result screen.
- Categories: add a
topicproperty (History, Science) and let the learner pick one before starting. - Keyboard support: let the keys 1 to 4 choose an option.
Common Mistakes
- Forgetting
clearInterval. Each call tosetIntervalcreates a new timer. If the old one is not stopped, both run, and the clock jumps by two each second. Run this small example to see two timers adding up:
let ticks = 0;
// Each call to start() makes a NEW interval. Forgetting clearInterval
// means the old one keeps running too.
function start() {
setInterval(() => {
ticks++;
}, 100);
}
start();
start(); // a second timer, without stopping the first
setTimeout(() => {
console.log(`Ticks after 1 second: ${ticks}`); // about 20, not 10
}, 1050);
- Counting the answer index from 1. If you write
answer: 3for the third option, the quiz marks the wrong button as correct. Arrays start at 0. - Allowing many clicks on one question. Without disabling the buttons, Rahul could click every option and collect points. Fix: disable all options inside
revealAnswer. - Not resetting everything on Play again. If you reset
currentbut notscore, the second round starts with the old score. Reset every state variable in one place.
Practice Task: Best Score and a Red Timer
Rahul wants to beat himself every day. Add two features to the complete app:
- Keep the best percentage so far in a variable, and show “Best score so far: 80%” on the result screen. Use
Math.max. - Make the timer red when 5 seconds or less are left, using the ready
warningclass andclassList.toggle. - Make sure the timer is not red at the start of the next question.
The HTML and CSS are ready. Complete the three TODO comments.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.big { font-size: 2.5rem; font-weight: 700; color: #4f46e5; margin: 8px 0; }
[hidden] { display: none !important; }
.warning { color: #dc2626; font-weight: 700; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<section id="quiz-screen">
<div class="top">
<span id="progress"></span>
<span id="timer">Time: 15s</span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</section>
<section id="result-screen" hidden>
<p>Your result</p>
<p class="big" id="result-percent"></p>
<p id="result-text"></p>
<p id="result-message"></p>
<p id="best"></p>
<button type="button" class="main-btn" id="play-again">Play again</button>
</section>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
const progressEl = document.querySelector('#progress');
const nextBtn = document.querySelector('#next-btn');
const scoreEl = document.querySelector('#score');
const timerEl = document.querySelector('#timer');
const quizScreen = document.querySelector('#quiz-screen');
const resultScreen = document.querySelector('#result-screen');
const resultPercent = document.querySelector('#result-percent');
const resultText = document.querySelector('#result-text');
const resultMessage = document.querySelector('#result-message');
const playAgainBtn = document.querySelector('#play-again');
const bestEl = document.querySelector('#best');
const TIME_LIMIT = 15;
let current = 0;
let score = 0;
let timeLeft = TIME_LIMIT;
let timerId = null;
// TODO 1: add a variable for the best percentage, starting at 0.
function showQuestion() {
const q = questions[current];
progressEl.textContent = `Question ${current + 1} of ${questions.length}`;
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
nextBtn.disabled = true;
nextBtn.textContent = current === questions.length - 1 ? 'See result' : 'Next';
startTimer();
}
function startTimer() {
clearInterval(timerId);
timeLeft = TIME_LIMIT;
timerEl.textContent = `Time: ${timeLeft}s`;
timerId = setInterval(() => {
timeLeft--;
timerEl.textContent = `Time: ${timeLeft}s`;
// TODO 2: add the 'warning' class when 5 seconds or less are left.
if (timeLeft === 0) {
timerEl.textContent = "Time's up!";
revealAnswer(null);
}
}, 1000);
}
function revealAnswer(chosenIndex) {
clearInterval(timerId);
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
if (chosenIndex === correctIndex) {
score++;
scoreEl.textContent = `Score: ${score}`;
}
nextBtn.disabled = false;
}
function showResult() {
clearInterval(timerId);
const percent = Math.round((score / questions.length) * 100);
resultPercent.textContent = `${percent}%`;
resultText.textContent = `You answered ${score} out of ${questions.length} correctly.`;
if (percent >= 80) {
resultMessage.textContent = 'Excellent! You are exam ready.';
} else if (percent >= 50) {
resultMessage.textContent = 'Good effort. Revise once more and try again.';
} else {
resultMessage.textContent = 'Keep practising. Every attempt makes you better.';
}
// TODO 3: update the best percentage and show it in bestEl.
quizScreen.hidden = true;
resultScreen.hidden = false;
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
nextBtn.addEventListener('click', () => {
current++;
if (current < questions.length) {
showQuestion();
} else {
showResult();
}
});
playAgainBtn.addEventListener('click', () => {
current = 0;
score = 0;
scoreEl.textContent = 'Score: 0';
resultScreen.hidden = true;
quizScreen.hidden = false;
showQuestion();
});
showQuestion();
</script>
</body>
</html>
Show one possible answer (try it yourself first!)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GK Quiz</title>
<style>
body { font-family: system-ui, sans-serif; background: #eef2ff; color: #1f2937; margin: 0; padding: 24px 16px; }
.quiz { max-width: 460px; margin: 0 auto; background: #ffffff; border-radius: 12px; padding: 20px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08); }
h1 { font-size: 1.3rem; margin: 0 0 12px; }
.top { display: flex; justify-content: space-between; color: #6b7280; font-size: 0.95rem; }
.question { font-size: 1.15rem; font-weight: 600; margin: 16px 0; }
.options { display: grid; gap: 10px; margin-bottom: 16px; }
.option { text-align: left; padding: 12px; border: 2px solid #e5e7eb; border-radius: 8px; background: #ffffff; color: #1f2937; font-size: 1rem; cursor: pointer; }
.option:hover:not(:disabled) { border-color: #4f46e5; }
.option:disabled { cursor: default; color: #1f2937; }
.option.correct { background: #dcfce7; border-color: #16a34a; }
.option.wrong { background: #fee2e2; border-color: #dc2626; }
.main-btn { background: #4f46e5; color: #ffffff; border: none; border-radius: 8px; padding: 10px 18px; font-size: 1rem; cursor: pointer; }
.main-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.big { font-size: 2.5rem; font-weight: 700; color: #4f46e5; margin: 8px 0; }
[hidden] { display: none !important; }
.warning { color: #dc2626; font-weight: 700; }
</style>
</head>
<body>
<div class="quiz">
<h1>GK Practice Quiz</h1>
<section id="quiz-screen">
<div class="top">
<span id="progress"></span>
<span id="timer">Time: 15s</span>
<span id="score">Score: 0</span>
</div>
<p class="question" id="question"></p>
<div class="options" id="options"></div>
<button type="button" class="main-btn" id="next-btn" disabled>Next</button>
</section>
<section id="result-screen" hidden>
<p>Your result</p>
<p class="big" id="result-percent"></p>
<p id="result-text"></p>
<p id="result-message"></p>
<p id="best"></p>
<button type="button" class="main-btn" id="play-again">Play again</button>
</section>
</div>
<script>
const questions = [
{
question: 'What is the capital of India?',
options: ['Mumbai', 'Kolkata', 'New Delhi', 'Chennai'],
answer: 2
},
{
question: 'What is the national animal of India?',
options: ['Asiatic lion', 'Bengal tiger', 'Indian elephant', 'Peacock'],
answer: 1
},
{
question: 'In which year was the Indian rupee symbol adopted?',
options: ['2005', '2008', '2010', '2014'],
answer: 2
},
{
question: 'Which is the largest planet in our solar system?',
options: ['Earth', 'Saturn', 'Mars', 'Jupiter'],
answer: 3
},
{
question: 'At what temperature does water boil at sea level?',
options: ['50 °C', '90 °C', '100 °C', '120 °C'],
answer: 2
}
];
const questionEl = document.querySelector('#question');
const optionsEl = document.querySelector('#options');
const progressEl = document.querySelector('#progress');
const nextBtn = document.querySelector('#next-btn');
const scoreEl = document.querySelector('#score');
const timerEl = document.querySelector('#timer');
const quizScreen = document.querySelector('#quiz-screen');
const resultScreen = document.querySelector('#result-screen');
const resultPercent = document.querySelector('#result-percent');
const resultText = document.querySelector('#result-text');
const resultMessage = document.querySelector('#result-message');
const playAgainBtn = document.querySelector('#play-again');
const bestEl = document.querySelector('#best');
const TIME_LIMIT = 15;
let current = 0;
let score = 0;
let timeLeft = TIME_LIMIT;
let timerId = null;
let bestPercent = 0;
function showQuestion() {
const q = questions[current];
progressEl.textContent = `Question ${current + 1} of ${questions.length}`;
questionEl.textContent = q.question;
optionsEl.innerHTML = '';
q.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'option';
btn.textContent = option;
btn.dataset.index = index;
optionsEl.append(btn);
});
nextBtn.disabled = true;
nextBtn.textContent = current === questions.length - 1 ? 'See result' : 'Next';
startTimer();
}
function startTimer() {
clearInterval(timerId);
timeLeft = TIME_LIMIT;
timerEl.textContent = `Time: ${timeLeft}s`;
timerEl.classList.remove('warning');
timerId = setInterval(() => {
timeLeft--;
timerEl.textContent = `Time: ${timeLeft}s`;
timerEl.classList.toggle('warning', timeLeft <= 5);
if (timeLeft === 0) {
timerEl.textContent = "Time's up!";
revealAnswer(null);
}
}, 1000);
}
function revealAnswer(chosenIndex) {
clearInterval(timerId);
const correctIndex = questions[current].answer;
for (const btn of optionsEl.children) {
const index = Number(btn.dataset.index);
btn.disabled = true;
if (index === correctIndex) {
btn.classList.add('correct');
} else if (index === chosenIndex) {
btn.classList.add('wrong');
}
}
if (chosenIndex === correctIndex) {
score++;
scoreEl.textContent = `Score: ${score}`;
}
nextBtn.disabled = false;
}
function showResult() {
clearInterval(timerId);
const percent = Math.round((score / questions.length) * 100);
resultPercent.textContent = `${percent}%`;
resultText.textContent = `You answered ${score} out of ${questions.length} correctly.`;
if (percent >= 80) {
resultMessage.textContent = 'Excellent! You are exam ready.';
} else if (percent >= 50) {
resultMessage.textContent = 'Good effort. Revise once more and try again.';
} else {
resultMessage.textContent = 'Keep practising. Every attempt makes you better.';
}
bestPercent = Math.max(bestPercent, percent);
bestEl.textContent = `Best score so far: ${bestPercent}%`;
quizScreen.hidden = true;
resultScreen.hidden = false;
}
optionsEl.addEventListener('click', (event) => {
const btn = event.target.closest('.option');
if (!btn || btn.disabled) return;
revealAnswer(Number(btn.dataset.index));
});
nextBtn.addEventListener('click', () => {
current++;
if (current < questions.length) {
showQuestion();
} else {
showResult();
}
});
playAgainBtn.addEventListener('click', () => {
current = 0;
score = 0;
scoreEl.textContent = 'Score: 0';
resultScreen.hidden = true;
quizScreen.hidden = false;
showQuestion();
});
showQuestion();
</script>
</body>
</html>
Key Takeaways
- Store quiz questions as an array of objects, with the right answer saved as an index.
- Keep app state in a few clear variables (
current,score,timeLeft,timerId) and build the screen from them. - One function,
revealAnswer, handles both a click and a timeout, so the scoring logic is never repeated. setIntervalreturns an id. Always stop the old timer withclearIntervalbefore starting a new one.- Switch screens with the
hiddenproperty, and reset every state variable when the user plays again.