Project: Quiz App
Table of Contents + −
In the previous lesson, we built a weather app that fetched live data from an API. Now let’s build a quiz app that ties together everything you have practiced so far: an array of objects holds the questions, the DOM shows them on screen, events react to clicks, and a few variables keep track of where the player is.
🎯 What We’ll Build
We are building a multiple-choice quiz. The app shows one question at a time with a few answer buttons. When the player picks an answer, the app checks if it is right, adds to the score if it is, and moves on to the next question. After the last question, it shows a final score instead of another question.
The whole thing runs on three pieces of information that change as the player works through the quiz: which question they are on, what their score is, and whether the quiz has finished. Those changing values are called the app’s state.
What is state?
State is just the data that describes “where things are right now.” In this quiz, the current question number and the score are the state. When the state changes, we redraw the screen to match.
🧱 The HTML
The page needs a spot for the question, a spot for the answer options, a button to move forward, and a spot to show the score. Each part gets an id so the JavaScript can find it later.
<div id="quiz"> <div id="question">Question goes here</div> <div id="options"></div> <button id="next-btn">Next</button> <div id="score"></div></div>Let’s walk through what each element is for:
- The outer
<div id="quiz">wraps the whole quiz so we have one container to style and position. <div id="question">is where the question text goes. The placeholder text gets replaced as soon as the JavaScript runs.<div id="options">starts empty because we build the answer buttons in JavaScript, one fresh set per question.<button id="next-btn">lets the player move to the next question once they have answered.<div id="score">also starts empty and only fills in at the end, when the quiz is complete.
⚙️ The JavaScript
Here is the full quiz logic. Read through it once, then we will walk through each part below.
const questions = [ { question: "Which keyword declares a value that cannot be reassigned?", options: ["var", "let", "const", "static"], correctAnswer: "const", }, { question: "What does the '===' operator check?", options: ["Value only", "Value and type", "Type only", "Nothing"], correctAnswer: "Value and type", }, { question: "Which method adds an item to the end of an array?", options: ["push", "pop", "shift", "slice"], correctAnswer: "push", },];
const questionEl = document.getElementById("question");const optionsEl = document.getElementById("options");const nextBtn = document.getElementById("next-btn");const scoreEl = document.getElementById("score");
let currentIndex = 0;let score = 0;
function renderQuestion() { const current = questions[currentIndex]; questionEl.textContent = current.question; optionsEl.innerHTML = "";
current.options.forEach((option) => { const button = document.createElement("button"); button.textContent = option; button.addEventListener("click", () => handleAnswer(option)); optionsEl.appendChild(button); });}
function handleAnswer(selected) { const current = questions[currentIndex]; if (selected === current.correctAnswer) { score++; } nextBtn.disabled = false;}
function showFinalScore() { questionEl.textContent = "Quiz complete!"; optionsEl.innerHTML = ""; nextBtn.style.display = "none"; scoreEl.textContent = `You scored ${score} out of ${questions.length}.`;}
nextBtn.addEventListener("click", () => { currentIndex++; if (currentIndex < questions.length) { renderQuestion(); } else { showFinalScore(); }});
renderQuestion();Let’s go through this top to bottom, one section at a time:
- The
questionsarray holds the whole quiz. Each item is an object with three properties: thequestiontext, anoptionsarray of possible answers, and thecorrectAnswerthat one of those options must match. - The four
getElementByIdlines grab the question, options, button, and score elements once and save them inconstvariables so we never have to look them up again. let currentIndex = 0andlet score = 0are the state: the question we are on and how many the player has gotten right. They start at zero and change as the quiz runs.renderQuestiondraws the current question. It readsquestions[currentIndex]to find which question to show, sets the question text, and clears any old buttons out of the options div withoptionsEl.innerHTML = "".- Inside
renderQuestion, theforEachloop builds a fresh button for each option, gives it a click listener that callshandleAnswerwith that button’s text, and appends it to the options div. handleAnswerruns when the player clicks an option. It compares the selected answer against the question’scorrectAnswer, adds one toscoreif they match, and enables the Next button so the player can move on.showFinalScoreswaps the question for a completion message, clears the options, hides the Next button, and prints the final tally in the score div.- The
nextBtnlistener bumpscurrentIndexup by one, then callsrenderQuestionif there are still questions left orshowFinalScoreonce we have run past the last one. - The final
renderQuestion()call kicks everything off by drawing the very first question when the page loads.
Why clear the options div?
Setting optionsEl.innerHTML = "" at the start of renderQuestion removes
the previous question’s buttons. Without it, old answers would pile up on
screen every time you moved forward.
🚀 Try It Yourself!
The quiz works, but there is plenty of room to make it your own. Try these enhancements.
- Add a countdown timer. Start a
setIntervalwhen each question renders, and callhandleAnswerwith a wrong answer (or skip ahead) when the time runs out. Clear the interval whenever the player answers. - Shuffle the questions so the quiz is different each time. Write a small function that reorders the
questionsarray before the firstrenderQuestioncall. - Add more questions to the array. Because the code loops over
questions.length, you do not have to change any logic, just add new objects. - Highlight the chosen answer. In
handleAnswer, color the clicked button green if it was right and red if it was wrong before moving on.
The array drives everything
Notice how adding a question never means touching the loop or the buttons. The data describes the quiz, and the code just reads the data. That separation is what makes the app easy to grow.
🧩 What You’ve Learned
- ✅ How to store structured data as an array of objects, each with its own properties
- ✅ How to render the current item to the DOM and rebuild it on every step
- ✅ How to attach event listeners to buttons you create in JavaScript
- ✅ How to track state with variables like
currentIndexandscore - ✅ How to end a flow by showing a final result instead of more content
🚀 What’s Next?
You have now combined data, the DOM, events, and state into one working app. Next we will track money instead of answers. Let’s continue to Expense Tracker.