Build an Interactive Quiz App

Build a multiple-choice quiz app using vanilla JavaScript and DOM manipulation.

Introduction

30 Second Summary

Every app you interact with online responds to your clicks, updates what you see, and keeps track of what's happening behind the scenes.

In this project, you will build an interactive quiz app from scratch using HTML, CSS, and JavaScript. The app displays questions one at a time, responds to your answers with instant colour feedback, and tracks your score across all five questions.

What You'll Build

A multiple-choice quiz app that runs entirely in your browser, built with vanilla JavaScript, HTML, and CSS.

By the end of this project, you'll have:

  • A working quiz app that displays questions, checks your answers with green and red highlights, and shows your final score.
  • Hands-on experience with DOM manipulation and event handling, the core skills every front-end developer interview tests for.
  • A clean project structure (index.html, style.css, script.js) that mirrors real-world web development.
  • Secret Mission: Add a countdown timer and a persistent high score board using localStorage.

Are there any prerequisites?

None. This project starts from scratch and assumes no prior JavaScript experience. You just need VS Code and a web browser already installed on your computer.

Before We Start

Before you start building, you need a clean workspace. Every web project starts with a folder structure that separates your content (HTML), styling (CSS), and logic (JavaScript) into their own files.

In this step, you will verify your development tools are ready and create the project folder that holds your quiz app.

In this step, get ready to:
  • Verify Node.js is installed on your machine.
  • Create a project folder with three files for HTML, CSS, and JavaScript.
  • Open the project in VS Code.
Open a terminal and verify Node.js

A terminal lets you run commands to create files, install tools, and manage your project. You will use it to confirm that Node.js is installed on your machine.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or Command Prompt (Windows) and press Enter to open it.
  • Verify Node.js is installed by running this command:
node --version

You should see a version number printed in the terminal, like v24.17.0.

✔️ I see a version number

Node.js is ready on your machine. You can move on to creating your project folder.

ⓧ Command not found

This means Node.js is not installed yet.

  • Go to https://nodejs.org and download the LTS version.
  • Run the installer and accept all the default options.
  • Close and reopen your terminal, then run node --version again to confirm it works.
Create the project folder and files

Your quiz app needs three files. One for the page structure (HTML), one for the visual design (CSS), and one for the interactive logic (JavaScript).

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir js-quiz-app
cd js-quiz-app
  • Create the three project files by running this command:
touch index.html style.css script.js
  • Confirm the files exist by running:
ls

You should see index.html, style.css, and script.js in the output.

Don't see all three files?

Make sure you typed the filenames correctly in the touch command. All three names should be separated by spaces with no commas.

If you are on Windows and touch is not recognized, create each file individually with:

New-Item index.html
New-Item style.css
New-Item script.js

help me create files in my terminal

Open the project in VS Code

VS Code is the code editor where you will write all your HTML, CSS, and JavaScript. Opening the project folder in VS Code lets you see and edit all three files from one window.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • In VS Code, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the js-quiz-app folder, and click Open.

You should see index.html, style.css, and script.js listed in the Explorer sidebar on the left.

Don't see your files in the sidebar?

Make sure you opened the js-quiz-app folder itself, not the Desktop folder above it. The folder name should appear at the top of the Explorer sidebar.

If the Explorer sidebar is not visible, press Cmd+Shift+E (macOS) or Ctrl+Shift+E (Windows) to toggle it open.

help me open my project folder in VS Code

Your workspace is ready. Next up, you will build the HTML structure that gives your quiz app its bones.

Set Up Your Project

Every web app starts with three files working together. HTML provides the structure, CSS handles the styling, and JavaScript adds interactivity.

Your js-quiz-app folder already has all three files ready to go. In this step, you'll set up index.html as the foundation that ties everything together.

In this step, get ready to:
  • Generate HTML boilerplate with the Emmet shortcut.
  • Connect your CSS and JavaScript files to the HTML.
  • Verify the page loads in your browser.
Generate and configure the HTML boilerplate

VS Code has a built-in tool called Emmet that expands shortcuts into full code snippets. You'll use it to generate a complete HTML5 document structure in seconds.

  • In VS Code, click on index.html in the Explorer sidebar to open it.
  • Type ! and press Tab to generate the HTML boilerplate.

What is Emmet?

Emmet is a toolkit built into VS Code that expands short abbreviations into full code. The ! abbreviation generates a complete HTML5 document with <head>, <body>, and metadata tags already in place.

You should see a full HTML document appear with <title>Document</title> inside the <head> section.

  • Find <title>Document</title> and change Document to JavaScript Quiz App.
  • On the line after the <title> tag, add a <link> tag that references style.css.
  • Just before the closing </body> tag, add a <script> tag with src set to script.js.
  • After these changes, your file should match the code below:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <script src="script.js"></script>
</body>
</html>

What does this code do?

  • <link rel="stylesheet" href="style.css"> tells the browser to load your CSS file for styling.
  • <script src="script.js"></script> loads your JavaScript file. Placing it at the bottom of <body> ensures the HTML loads before the script runs.
  • Save index.html.
  • Open the file in your browser by right-clicking index.html in Finder (macOS) or File Explorer (Windows) and choosing Open with your browser.
  • You should see a blank white page with "JavaScript Quiz App" shown in the browser tab title.

Don't see 'JavaScript Quiz App' in the tab?

  • Make sure you saved index.html after making your changes.
  • Check that you changed the text between <title> and </title> to exactly JavaScript Quiz App.
  • If the Emmet shortcut did not work, make sure you are editing a file with the .html extension and that the language mode in the bottom-right of VS Code shows HTML.

My browser tab still shows 'Document' instead of 'JavaScript Quiz App'. What did I miss?

✔️ Awesome, I've got everything!

Great. Double check you've saved your file and the browser tab reads "JavaScript Quiz App".

ⓧ I'd like to double check the full code

Here is the complete index.html at this point:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <script src="script.js"></script>
</body>
</html>

Your HTML foundation is ready with CSS and JavaScript connected. Next up, you'll build the quiz structure with headings, buttons, and containers that JavaScript will bring to life.

Build the HTML Structure

Your project files are set up and your blank page is loading in the browser. Now it is time to give the quiz app its skeleton.

HTML defines what elements exist on the page before any styling or logic runs. You will create the containers, headings, buttons, and placeholders that JavaScript will fill in later.

In this step, get ready to:
  • Build the question display area with choice buttons.
  • Build the results section that shows the final score.
Create the quiz question area

The quiz needs a place to display the current question, the answer choices, and a button to move to the next question. You will build all of these inside a single container.

  • In VS Code, open index.html.
  • Find the <script src="script.js"></script> line.
  • Add the following code above that line:
    <div class="quiz-container">
        <h1>JavaScript Quiz</h1>
        <div id="quiz">
            <div id="question-container">
                <p id="question-number">Question 1 of 5</p>
                <h2 id="question-text"></h2>
            </div>
            <div id="choices-container"></div>
            <button id="next-btn">Next Question</button>
        </div>
    </div>

What does this code do?

  • quiz-container is the outer wrapper that holds the entire app. You will use this later for centering and styling.
  • quiz is the active quiz section. JavaScript will show this div while questions remain.
  • question-container holds the question number and question text. JavaScript will update these for each new question.
  • choices-container is empty because JavaScript will create answer buttons dynamically.
  • next-btn advances to the next question after the learner picks an answer.
  • Save index.html.
  • Refresh your browser. You should see "JavaScript Quiz" as a heading, "Question 1 of 5" as text, and a "Next Question" button.

Don't see the heading or button?

  • Make sure you pasted the code above the <script> tag, not below it or inside it.
  • Check that every opening <div> has a matching closing </div>. A missing closing tag can hide everything after it.
  • Confirm you saved the file before refreshing the browser.

Why isn't my HTML showing up in the browser?

Add the results section

When the learner finishes all five questions, the app needs to display a final score and a restart button. This section starts hidden and only appears after the last question.

  • In index.html, find the closing </div> that corresponds to the #quiz div (the one right after the next-btn button).
  • Add the following code below it, but still inside the quiz-container closing </div>:
        <div id="results" class="hidden">
            <h2>Quiz Complete!</h2>
            <p id="score-text"></p>
            <button id="restart-btn">Try Again</button>
        </div>

What does this code do?

  • The hidden class will hide this entire section with CSS. JavaScript will remove that class when the quiz is over.
  • score-text is an empty paragraph that JavaScript will fill with the final score.
  • restart-btn resets the quiz so the user can play again.
  • Save index.html and refresh your browser.
  • You should see "JavaScript Quiz" as a heading, the text "Next Question" and "Try Again" as buttons, and "Quiz Complete!" visible on the page.

The page looks messy without CSS. That is expected.

Missing the results section?

  • Make sure you pasted the results code between the closing </div> of #quiz and the closing </div> of quiz-container. If it is outside the container, it may not show correctly later.
  • Count your <div> and </div> tags. They should come in matching pairs.

Help me figure out why part of my HTML is missing.

Why is "Quiz Complete!" showing already?

Right now, the hidden class is just a name with no style definition yet. Once you add CSS in the next step, .hidden will set display: none and the results section will disappear until JavaScript reveals it.

✔️ Awesome, I've got everything!

Great. Double check you have saved your file before moving on.

ⓧ I'd like to double check the full code

Here is the complete index.html file at this point:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Quiz App</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="quiz-container">
        <h1>JavaScript Quiz</h1>
        <div id="quiz">
            <div id="question-container">
                <p id="question-number">Question 1 of 5</p>
                <h2 id="question-text"></h2>
            </div>
            <div id="choices-container"></div>
            <button id="next-btn">Next Question</button>
        </div>
        <div id="results" class="hidden">
            <h2>Quiz Complete!</h2>
            <p id="score-text"></p>
            <button id="restart-btn">Try Again</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

Your HTML skeleton is in place. Every element the quiz needs exists on the page. Next up, you will bring this structure to life with CSS styling.

Style the Quiz with CSS

Your HTML structure is in place, but right now the quiz looks like a jumble of unstyled text and buttons. CSS transforms that raw structure into a polished, dark-themed interface.

In this step, you will add styles that center the quiz card on screen, give the choices a clickable appearance, and prepare visual feedback classes for correct and incorrect answers.

In this step, get ready to:
  • Add a universal reset and dark-themed page layout.
  • Style the quiz choices and add visual feedback classes for correct, incorrect, and disabled states.
  • Style the navigation buttons and add utility classes.
Add the base layout styles

Every web page starts with default browser styles that add unwanted margins and padding. A universal reset removes those defaults so you start from a clean slate.

  • In VS Code, open style.css.
  • Add the universal reset, body styles, and quiz container styles by pasting this code:
/* Remove default browser margins and padding */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* Dark background with centered content */
body {
    font-family: Arial, sans-serif;
    background-color: #1a1a2e;
    color: #ffffff;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

/* The main quiz card */
.quiz-container {
    background-color: #16213e;
    border-radius: 12px;
    padding: 40px;
    max-width: 600px;
    width: 90%;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

What does this code do?

The * selector targets every element on the page. Setting box-sizing: border-box makes padding count inside the element's width instead of adding to it.

Flexbox on the body (display: flex with justify-content: center and align-items: center) centers the quiz card both horizontally and vertically. The min-height: 100vh makes the body fill the entire viewport height.

  • Save style.css.
  • Refresh your browser. You should see a dark navy page with a slightly lighter card centered in the middle.

Still seeing a white background?

Check that your index.html has <link rel="stylesheet" href="style.css"> in the <head> section. The file name must match exactly, including the lowercase spelling.

Help me debug why my CSS file isn't loading.

Style the question area and choice buttons

The quiz heading needs to stand out, and the answer choices need to look like clickable buttons. You will also prepare CSS classes that JavaScript will add later to show correct and incorrect answers.

  • Below the .quiz-container block you just wrote, add the heading, question, and choice button styles:
/* Quiz title in accent color */
h1 {
    text-align: center;
    margin-bottom: 30px;
    color: #e94560;
}

/* Small gray text above the question */
#question-number {
    color: #a0a0a0;
    margin-bottom: 10px;
    font-size: 14px;
}

/* The question itself */
#question-text {
    font-size: 20px;
    margin-bottom: 20px;
    line-height: 1.4;
}

/* Stack choices vertically with spacing */
#choices-container {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 20px;
}

/* Each answer choice button */
.choice-btn {
    padding: 14px 20px;
    border: 2px solid #0f3460;
    border-radius: 8px;
    background-color: #0f3460;
    color: #ffffff;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s, border-color 0.3s;
    text-align: left;
}

/* Highlight border on hover */
.choice-btn:hover {
    background-color: #1a4a7a;
    border-color: #e94560;
}

/* Green for correct answers */
.choice-btn.correct {
    background-color: #2ecc71;
    border-color: #2ecc71;
}

/* Red for incorrect answers */
.choice-btn.incorrect {
    background-color: #e74c3c;
    border-color: #e74c3c;
}

/* Prevent clicks after answering */
.choice-btn.disabled {
    pointer-events: none;
    opacity: 0.7;
}

What does this code do?

The .choice-btn class styles each answer choice as a dark blue button with smooth hover transitions. The cursor: pointer property changes the mouse icon to a hand when hovering, signaling that the element is clickable.

The .correct, .incorrect, and .disabled classes are not applied to anything yet. JavaScript will add them dynamically later to give visual feedback when the user picks an answer.

  • Save style.css and refresh your browser.
  • You should see "JavaScript Quiz" in a pink-red accent color and "Question 1 of 5" in small gray text.

Heading still white?

Make sure the new code goes below the closing } of .quiz-container, not inside it. Each CSS rule needs its own set of curly braces.

Help me figure out why my CSS styles aren't applying.

Style the navigation buttons and utility classes

The "Next Question" and "Try Again" buttons need consistent styling. You will also add a utility class that hides elements, and set the next button to be invisible by default until JavaScript shows it.

  • Below the .choice-btn.disabled block, add the button and utility styles:
/* Shared styles for both navigation buttons */
#next-btn,
#restart-btn {
    display: block;
    width: 100%;
    padding: 14px;
    border: none;
    border-radius: 8px;
    background-color: #e94560;
    color: #ffffff;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s;
}

#next-btn:hover,
#restart-btn:hover {
    background-color: #c0392b;
}

/* Hide Next button until an answer is selected */
#next-btn {
    display: none;
}

/* Results section centered */
#results {
    text-align: center;
}

#results h2 {
    margin-bottom: 20px;
    color: #e94560;
}

#score-text {
    font-size: 24px;
    margin-bottom: 30px;
}

/* Utility class to hide elements */
.hidden {
    display: none;
}

What does this code do?

The #next-btn rule sets display: none so the "Next Question" button is invisible until JavaScript changes it to display: block after the user picks an answer.

The .hidden class is a reusable utility. Any element with class="hidden" disappears from the page. Your results div already has this class in the HTML, so it is now hidden.

  • Save style.css and refresh your browser.
  • The page should have a dark background with a centered card showing "JavaScript Quiz" in pink-red and "Question 1 of 5" in gray. The "Next Question" button, "Quiz Complete!" text, and "Try Again" button should all be hidden.

Still seeing "Quiz Complete!" on the page?

Check that your index.html has class="hidden" on the results div. Also confirm the .hidden class at the bottom of your CSS file has display: none inside its curly braces.

Help me figure out why my hidden class isn't working.

✔️ Awesome, I've got everything!

Great. Double check you have saved style.css before moving on.

ⓧ I'd like to double check the full code

Here is the complete style.css file. Make sure yours matches exactly.

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: Arial, sans-serif;
    background-color: #1a1a2e;
    color: #ffffff;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

.quiz-container {
    background-color: #16213e;
    border-radius: 12px;
    padding: 40px;
    max-width: 600px;
    width: 90%;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

h1 {
    text-align: center;
    margin-bottom: 30px;
    color: #e94560;
}

#question-number {
    color: #a0a0a0;
    margin-bottom: 10px;
    font-size: 14px;
}

#question-text {
    font-size: 20px;
    margin-bottom: 20px;
    line-height: 1.4;
}

#choices-container {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 20px;
}

.choice-btn {
    padding: 14px 20px;
    border: 2px solid #0f3460;
    border-radius: 8px;
    background-color: #0f3460;
    color: #ffffff;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s, border-color 0.3s;
    text-align: left;
}

.choice-btn:hover {
    background-color: #1a4a7a;
    border-color: #e94560;
}

.choice-btn.correct {
    background-color: #2ecc71;
    border-color: #2ecc71;
}

.choice-btn.incorrect {
    background-color: #e74c3c;
    border-color: #e74c3c;
}

.choice-btn.disabled {
    pointer-events: none;
    opacity: 0.7;
}

#next-btn,
#restart-btn {
    display: block;
    width: 100%;
    padding: 14px;
    border: none;
    border-radius: 8px;
    background-color: #e94560;
    color: #ffffff;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.3s;
}

#next-btn:hover,
#restart-btn:hover {
    background-color: #c0392b;
}

#next-btn {
    display: none;
}

#results {
    text-align: center;
}

#results h2 {
    margin-bottom: 20px;
    color: #e94560;
}

#score-text {
    font-size: 24px;
    margin-bottom: 30px;
}

.hidden {
    display: none;
}

Your quiz looks sharp. Next up, you will write JavaScript to load questions from a data structure and display them dynamically on the page.

Create Quiz Data and Display Questions

Your quiz app looks great with the dark theme and styled buttons. Now it's time to bring it to life with JavaScript.

Right now the quiz card is empty because there's no data or logic powering it. In this step, you'll create the quiz questions as structured data and write a function that reads that data to render questions on screen.

In this step, get ready to:
  • Define quiz questions as an array of objects.
  • Cache DOM element references for fast manipulation.
  • Build a function that dynamically displays questions and choices.
Create the questions array and state variables

Every quiz needs data. You'll store the questions as an array of objects, where each object holds the question text, four answer choices, and the index of the correct answer.

  • In VS Code, open script.js from the Explorer sidebar.
  • Add the quiz data and state variables by pasting this code:
var questions = [
    {
        question: "What keyword is used to declare a variable that can be reassigned?",
        choices: ["const", "let", "var", "Both let and var"],
        correctAnswer: 3
    },
    {
        question: "What does DOM stand for?",
        choices: ["Document Object Model", "Data Object Manager", "Digital Output Mode", "Document Order Method"],
        correctAnswer: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        choices: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correctAnswer: 2
    },
    {
        question: "What is the result of typeof null in JavaScript?",
        choices: ["'null'", "'undefined'", "'object'", "'boolean'"],
        correctAnswer: 2
    },
    {
        question: "Which array method adds an item to the end of an array?",
        choices: [".pop()", ".shift()", ".push()", ".unshift()"],
        correctAnswer: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

What does this code do?

The questions array holds 5 objects. Each object has three properties: question (the text shown to the user), choices (an array of 4 possible answers), and correctAnswer (the index of the right answer in the choices array).

The two variables at the bottom track the quiz state. currentQuestionIndex keeps track of which question the user is on. score counts how many correct answers they've given.

Why use an array of objects?

Storing quiz data as an array of objects is a common pattern in real-world apps. Each object groups related data together (one question with its choices). The array lets you loop through all questions with a simple index. This same data-modeling approach works for product catalogs, user lists, and API responses.

  • Save script.js.

You won't see a visible change in the browser yet. The next chunk uses this data to render questions on screen.

Cache DOM element references

Before you can update the page, JavaScript needs to know which HTML elements to target. You'll use document.getElementById() to grab each element once and store it in a variable for fast reuse.

  • Below the var score = 0; line, add these DOM references:
var questionText = document.getElementById("question-text");
var questionNumber = document.getElementById("question-number");
var choicesContainer = document.getElementById("choices-container");
var nextBtn = document.getElementById("next-btn");
var quiz = document.getElementById("quiz");
var results = document.getElementById("results");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

What does this code do?

Each line grabs a specific HTML element by its id attribute and stores it in a variable. For example, document.getElementById("question-text") finds the <h2> element where the question text will appear.

Caching these references up front means your code doesn't have to search the entire page every time it needs to update something. This is a best practice in DOM manipulation.

  • Save script.js.

You won't see a change yet. The next chunk uses these variables to render questions dynamically.

Build the loadQuestion function

Now you'll create the function that actually puts a question on screen. It reads from the questions array, updates the text elements, and dynamically creates a button for each answer choice.

  • Below the DOM references, add the loadQuestion function and its initial call:
function loadQuestion() {
    var currentQuestion = questions[currentQuestionIndex];
    questionText.textContent = currentQuestion.question;
    questionNumber.textContent = "Question " + (currentQuestionIndex + 1) + " of " + questions.length;
    choicesContainer.innerHTML = "";
    nextBtn.style.display = "none";

    currentQuestion.choices.forEach(function (choice, index) {
        var button = document.createElement("button");
        button.textContent = choice;
        button.classList.add("choice-btn");
        button.addEventListener("click", function () {
            checkAnswer(index);
        });
        choicesContainer.appendChild(button);
    });
}

loadQuestion();

What does this code do?

  • currentQuestion grabs the current question object from the array using the index variable.
  • textContent updates the question heading and the "Question X of 5" counter.
  • choicesContainer.innerHTML = "" wipes any old buttons so they don't stack up.
  • The forEach loop creates a new button for each choice, styles it with choice-btn, attaches a click listener, and appends it to the container.
  • The final loadQuestion() call at the bottom runs the function immediately when the page loads.
  • Save script.js and refresh your browser.

You should see "Question 1 of 5" and the first question text with four clickable choice buttons. Clicking a choice does nothing yet. That is expected.

Don't see any questions?

  • Make sure you saved script.js. Unsaved changes won't run in the browser.
  • Open the browser console (press F12 then click Console) and check for red error messages. A typo in an element ID will cause the script to fail.
  • Verify the <script src="script.js"></script> tag is at the bottom of your index.html body, just before </body>.

Help me debug why my quiz questions aren't showing up.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

Here is the complete script.js at this point:

var questions = [
    {
        question: "What keyword is used to declare a variable that can be reassigned?",
        choices: ["const", "let", "var", "Both let and var"],
        correctAnswer: 3
    },
    {
        question: "What does DOM stand for?",
        choices: ["Document Object Model", "Data Object Manager", "Digital Output Mode", "Document Order Method"],
        correctAnswer: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        choices: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correctAnswer: 2
    },
    {
        question: "What is the result of typeof null in JavaScript?",
        choices: ["'null'", "'undefined'", "'object'", "'boolean'"],
        correctAnswer: 2
    },
    {
        question: "Which array method adds an item to the end of an array?",
        choices: [".pop()", ".shift()", ".push()", ".unshift()"],
        correctAnswer: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

var questionText = document.getElementById("question-text");
var questionNumber = document.getElementById("question-number");
var choicesContainer = document.getElementById("choices-container");
var nextBtn = document.getElementById("next-btn");
var quiz = document.getElementById("quiz");
var results = document.getElementById("results");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

function loadQuestion() {
    var currentQuestion = questions[currentQuestionIndex];
    questionText.textContent = currentQuestion.question;
    questionNumber.textContent = "Question " + (currentQuestionIndex + 1) + " of " + questions.length;
    choicesContainer.innerHTML = "";
    nextBtn.style.display = "none";

    currentQuestion.choices.forEach(function (choice, index) {
        var button = document.createElement("button");
        button.textContent = choice;
        button.classList.add("choice-btn");
        button.addEventListener("click", function () {
            checkAnswer(index);
        });
        choicesContainer.appendChild(button);
    });
}

loadQuestion();

Your quiz now displays questions and choices dynamically from data. Next up, you'll make those buttons actually do something by checking answers and tracking the score.

Handle Answers and Show Results

Your quiz can display questions and render choice buttons. Right now, clicking a button calls a function that does not exist yet.

In this step, you will bring the quiz to life. You will check whether the clicked answer is correct, give the player visual feedback, track their score, and show final results when the quiz ends.

In this step, get ready to:
  • Create answer-checking logic that highlights correct and incorrect choices.
  • Build results and restart functions to complete the quiz flow.
  • Wire up the Next Question and Try Again buttons with event listeners.
Create the checkAnswer function

Your choice buttons already call checkAnswer(index) when clicked. The function does not exist yet, so nothing happens. Time to define it.

  • In script.js, find the closing } of the loadQuestion function.
  • Add the following checkAnswer function below it (above the loadQuestion(); call at the bottom):
function checkAnswer(selectedIndex) {
    var currentQuestion = questions[currentQuestionIndex];
    var buttons = choicesContainer.querySelectorAll(".choice-btn");

    // Disable all buttons so the user can't click again
    buttons.forEach(function (button) {
        button.classList.add("disabled");
    });

    // Check if the selected answer is correct
    if (selectedIndex === currentQuestion.correctAnswer) {
        buttons[selectedIndex].classList.add("correct");
        score++;
    } else {
        buttons[selectedIndex].classList.add("incorrect");
        buttons[currentQuestion.correctAnswer].classList.add("correct");
    }

    // Show the next button so the user can advance
    nextBtn.style.display = "block";
}

What does this code do?

  • querySelectorAll(".choice-btn") grabs every choice button currently on the page so you can loop through them.
  • Adding the disabled class to every button prevents the user from clicking a second answer.
  • The if/else compares the clicked index to the correct answer. A match adds the green correct class and increments the score. A wrong answer adds the red incorrect class AND reveals the correct answer in green.
  • Finally, the Next Question button becomes visible so the player can move on.
  • Save script.js and refresh your browser.
  • Click any choice button. The button should highlight green (correct) or red (incorrect). The correct answer always appears in green. The Next Question button appears below the choices.

Clicking a button does nothing?

  • Make sure checkAnswer is placed ABOVE the loadQuestion(); call at the bottom. JavaScript reads top to bottom, so the function must exist before it is called.
  • Open DevTools (press F12) and check the Console tab for red error messages. A typo in the function name will show up there.
  • Help me debug why my checkAnswer function is not working.
Create the showResults and restartQuiz functions

After the last question, the quiz needs to hide the question area and display the player's final score. It also needs a way to start over.

  • In script.js, add the showResults function directly below checkAnswer:
function showResults() {
    // Hide the quiz and show the results section
    quiz.classList.add("hidden");
    results.classList.remove("hidden");
    scoreText.textContent = "You scored " + score + " out of " + questions.length + "!";
}

What does this code do?

This function swaps the visible sections. Adding the hidden class to quiz hides all the question content. Removing hidden from results reveals the score screen.

The score text uses string concatenation to build a message like "You scored 3 out of 5!".

  • Add the restartQuiz function directly below showResults:
function restartQuiz() {
    // Reset the quiz state back to the beginning
    currentQuestionIndex = 0;
    score = 0;
    quiz.classList.remove("hidden");
    results.classList.add("hidden");
    loadQuestion();
}

What does this code do?

This resets the two state variables (currentQuestionIndex and score) back to zero, then reverses the section swap so the quiz is visible again. Finally, it calls loadQuestion() to render question 1 fresh.

  • Save script.js.
  • You will not see a visible change yet. These functions are defined but nothing calls them until you add event listeners in the next substep.
Add event listeners to the navigation buttons

The Next Question and Try Again buttons exist in the HTML but have no behavior attached. Event listeners connect a user action (like a click) to a function that runs in response.

  • In script.js, add the following event listeners below the restartQuiz function (still above the final loadQuestion(); call):
// Advance to the next question or show results
nextBtn.addEventListener("click", function () {
    currentQuestionIndex++;
    if (currentQuestionIndex < questions.length) {
        loadQuestion();
    } else {
        showResults();
    }
});

// Restart the quiz from the beginning
restartBtn.addEventListener("click", restartQuiz);

What does this code do?

  • The nextBtn listener increments currentQuestionIndex and uses a conditional to decide what happens next. If there are remaining questions, it loads the next one. If the index has reached the end, it shows the results screen.
  • The restartBtn listener simply calls restartQuiz directly. When a function takes no arguments, you can pass its name without wrapping it in another function.
  • Save script.js and refresh your browser.
  • Play through all 5 questions. After clicking an answer, the Next Question button should appear. After the 5th answer, click Next Question to see the results screen with your score and a Try Again button.
  • Click Try Again. The quiz should restart from question 1 with a reset score.

Quiz doesn't advance or show results?

  • Make sure the event listeners are placed AFTER all function definitions but BEFORE the final loadQuestion(); call.
  • Check that you used nextBtn and restartBtn (matching the variable names from earlier). A mismatched name causes a silent failure.
  • Help me figure out why my quiz doesn't advance to the next question.

✔️ Awesome, I've got everything!

Great. Double check that you have saved script.js and that the full quiz loop works (answer, next, results, restart).

ⓧ I'd like to double check the full code

Here is the complete script.js at this point:

var questions = [
    {
        question: "What keyword is used to declare a variable that can be reassigned?",
        choices: ["const", "let", "var", "Both let and var"],
        correctAnswer: 3
    },
    {
        question: "What does DOM stand for?",
        choices: ["Document Object Model", "Data Object Manager", "Digital Output Mode", "Document Order Method"],
        correctAnswer: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        choices: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correctAnswer: 2
    },
    {
        question: "What is the result of typeof null in JavaScript?",
        choices: ["'null'", "'undefined'", "'object'", "'boolean'"],
        correctAnswer: 2
    },
    {
        question: "Which array method adds an item to the end of an array?",
        choices: [".pop()", ".shift()", ".push()", ".unshift()"],
        correctAnswer: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

var questionText = document.getElementById("question-text");
var questionNumber = document.getElementById("question-number");
var choicesContainer = document.getElementById("choices-container");
var nextBtn = document.getElementById("next-btn");
var quiz = document.getElementById("quiz");
var results = document.getElementById("results");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

function loadQuestion() {
    var currentQuestion = questions[currentQuestionIndex];
    questionText.textContent = currentQuestion.question;
    questionNumber.textContent = "Question " + (currentQuestionIndex + 1) + " of " + questions.length;
    choicesContainer.innerHTML = "";
    nextBtn.style.display = "none";

    currentQuestion.choices.forEach(function (choice, index) {
        var button = document.createElement("button");
        button.textContent = choice;
        button.classList.add("choice-btn");
        button.addEventListener("click", function () {
            checkAnswer(index);
        });
        choicesContainer.appendChild(button);
    });
}

function checkAnswer(selectedIndex) {
    var currentQuestion = questions[currentQuestionIndex];
    var buttons = choicesContainer.querySelectorAll(".choice-btn");

    buttons.forEach(function (button) {
        button.classList.add("disabled");
    });

    if (selectedIndex === currentQuestion.correctAnswer) {
        buttons[selectedIndex].classList.add("correct");
        score++;
    } else {
        buttons[selectedIndex].classList.add("incorrect");
        buttons[currentQuestion.correctAnswer].classList.add("correct");
    }

    nextBtn.style.display = "block";
}

function showResults() {
    quiz.classList.add("hidden");
    results.classList.remove("hidden");
    scoreText.textContent = "You scored " + score + " out of " + questions.length + "!";
}

function restartQuiz() {
    currentQuestionIndex = 0;
    score = 0;
    quiz.classList.remove("hidden");
    results.classList.add("hidden");
    loadQuestion();
}

nextBtn.addEventListener("click", function () {
    currentQuestionIndex++;
    if (currentQuestionIndex < questions.length) {
        loadQuestion();
    } else {
        showResults();
    }
});

restartBtn.addEventListener("click", restartQuiz);

loadQuestion();

Your quiz is fully playable. You have a complete loop of answering, scoring, viewing results, and restarting. Next up, you will push this further with a timed challenge and persistent high scores in the Secret Mission.

Secret mission

Add a Timer and High Scores

Your quiz works, but there is no time pressure. Real quizzes keep you on your toes. In this secret mission, you will add a 15-second countdown timer that auto-advances when time runs out, plus a high score board that remembers your best attempts across browser sessions.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your project files to continue experimenting, or delete them to free up space.

Resources you used:

  • The js-quiz-app folder on your Desktop (contains index.html, style.css, and script.js).
  • Browser localStorage data (the quizHighScores key storing your high score history).

Keep everything running

No action needed. Choose this if you want to keep experimenting with your quiz app or add more features.

  • Your js-quiz-app folder stays on your Desktop.
  • Your high scores remain saved in your browser's localStorage.
  • You can reopen the project anytime by opening index.html in your browser.

Pause - I'll come back to this later

Since this is a local project with no running processes, there is nothing to pause. Your files will be waiting for you whenever you return.

  • Close the js-quiz-app folder in VS Code by clicking File, then Close Folder.
  • Close the browser tab running the quiz.

Delete - I don't want to use this again

Remove all project files and stored data to start completely fresh.

  • Delete the project folder by running this command in your terminal:

macOS

rm -rf ~/Desktop/js-quiz-app

Windows

Remove-Item -Recurse -Force ~\Desktop\js-quiz-app
  • Clear your high score data from localStorage. Open your browser's developer console by pressing Cmd+Option+J (macOS) or Ctrl+Shift+J (Windows), then run:
localStorage.removeItem("quizHighScores")
  • Confirm the folder is gone by running ls ~/Desktop (macOS) or dir ~\Desktop (Windows). You should no longer see js-quiz-app in the output.

Nice Work!

Nice Work!

Nice work! You just built a complete interactive quiz app from scratch using HTML, CSS, and JavaScript.

You've learned how to:

  • Used arrays and objects to model structured quiz data, then manipulated the DOM to dynamically render questions and feedback.
  • Handled user events (clicks) and used conditionals to check answers and provide instant visual feedback.
  • Managed application state (score, current question index) across the entire quiz flow, including restart functionality.
  • Secret Mission: Added time pressure with setInterval and persistent high scores with localStorage.

Ready to quiz yourself?