Build an Interactive Quiz App

Build a browser-based quiz app using vanilla JavaScript and DOM manipulation.

Introduction

30 Second Summary

Every time you take an online quiz, code is running behind the scenes to check your answers, track your score, and show you results. That's JavaScript at work.

In this project, you will build an interactive quiz app from scratch using HTML, CSS, and JavaScript. Your app will present multiple-choice questions, give instant visual feedback on each answer, and display a final score.

What You'll Build

A browser-based multiple-choice quiz that presents five questions one at a time, highlights correct and incorrect answers with color, and tracks your score in real time.

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

  • A multi-screen web app with start, quiz, and results screens built with semantic HTML.
  • JavaScript logic that dynamically creates elements, handles click events, and updates your score in real time.
  • A dark-themed interface with smooth color transitions for correct and incorrect answers.
  • Secret Mission: Add a countdown timer to each question that auto-advances when time runs out.

Are there any prerequisites?

None. This guide includes full setup instructions for every tool you need. All you need is a computer running macOS or Windows.

Set Up Your Development Environment

Before writing any code, you need a clean project structure. Web apps separate their concerns into three types of files: HTML for the page structure, CSS for the styling, and JavaScript for the interactive behavior.

In this step, you'll create these three files inside your project folder. Once they're in place, you'll be ready to start building the quiz.

In this step, get ready to:
  • Create the three files that make up your quiz app.
  • Confirm your project structure is ready for coding.
Create your project files

Your quiz app needs one file for each responsibility. The HTML file defines what elements appear on the page. The CSS file controls how they look. The JavaScript file makes them interactive.

  • In VS Code, click Terminal in the top menu bar.
  • Click New Terminal.
  • Create your three project files by running this command:

macOS

touch index.html style.css script.js

Windows

New-Item -ItemType File -Name "index.html", "style.css", "script.js"

What does this command do?

This command creates three empty files in your project folder. Each file is a blank canvas ready for you to add code in the upcoming steps.

  • index.html will hold the quiz's page structure (headings, buttons, containers).
  • style.css will control colors, spacing, and layout.
  • script.js will contain the logic that makes the quiz respond to clicks and track your score.
  • Confirm the files exist by running:
ls

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

Don't see all three files?

  • Check for typos in the filenames. Make sure you typed index.html, style.css, and script.js exactly as shown.
  • Make sure your terminal is in the correct folder. Look at the terminal prompt to confirm it shows your project folder name.
  • If you see a permissions error, try closing and reopening VS Code. Then open your project folder again by clicking File and selecting Open Folder.

Still stuck? Help me figure out why my files weren't created in VS Code's terminal.

You should also see all three files appear in the Explorer sidebar on the left side of VS Code.

Your project structure is ready. Next up, you'll build the HTML skeleton that gives your quiz its three screens: start, questions, and results.

Build the HTML Structure

Your development environment is ready with VS Code and Live Server installed.

Now it's time to build the skeleton of your quiz app. HTML defines the structure and content that users see and interact with. Your quiz needs three distinct screens: a start screen, a question screen, and a results screen.

In this step, get ready to:
  • Build the HTML document with links to your CSS and JavaScript files.
  • Create three quiz screens: start, quiz, and results.
  • Add buttons, headings, and containers that JavaScript will control later.
Set up the document and start screen

Every HTML page needs a document structure that tells the browser how to render it. The <head> section contains metadata and file links. The <body> section contains everything the user sees.

  • In VS Code, click on index.html in the Explorer sidebar to open it.
  • Paste the following code into the empty file:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Quiz</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="quiz-container">
        <div id="start-screen">
            <h1>JavaScript Quiz</h1>
            <p>Test your knowledge with 5 questions!</p>
            <button id="start-btn">Start Quiz</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

What does this code do?

  • The <link rel="stylesheet" href="style.css"> tag connects your CSS file so styles will apply once you write them.
  • The quiz-container div wraps the entire app. All three screens live inside it.
  • The start-screen div is the first thing users see. It has a heading, a description, and a button to kick off the quiz.
  • The <script src="script.js"> tag at the bottom loads your JavaScript after the HTML is ready.
  • Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Click the Go Live button in the bottom-right corner of the VS Code status bar to start Live Server.
  • Your browser opens automatically. You should see the heading "JavaScript Quiz", the text "Test your knowledge with 5 questions!", and a "Start Quiz" button on a plain white page.

Why does the page look unstyled?

Your style.css file is still empty. The HTML is correctly linked to it, but there are no style rules yet. You will add the dark theme and button styles in the next step.

Don't see anything in the browser?

  • Make sure you saved index.html before clicking Go Live. Unsaved files show a dot on the tab in VS Code.
  • Check that index.html is inside your project folder, not at a different location.
  • If Go Live is not visible in the status bar, open the Command Palette with Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows) and type Live Server: Open with Live Server.

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

Add the quiz screen

The quiz screen is where the action happens. It shows the current question, answer buttons, a progress indicator, and a score counter. This screen starts hidden and only appears when the user clicks "Start Quiz."

  • In index.html, find the closing </div> tag of the start-screen section (the one right after the </button> tag).
  • Add the following code directly below that closing </div>, but before the closing </div> of quiz-container:
        <div id="quiz-screen" class="hidden">
            <div class="progress">
                <span id="question-number">Question 1 of 5</span>
                <span id="score">Score: 0</span>
            </div>
            <h2 id="question"></h2>
            <div id="answers"></div>
            <button id="next-btn" class="hidden">Next Question</button>
        </div>

What does this code do?

  • The class="hidden" on the quiz screen means it won't be visible by default. JavaScript will remove this class when the quiz starts.
  • The progress div shows the question number and score side by side.
  • The question heading and answers div are empty because JavaScript will fill them dynamically with each question.
  • The next-btn button also starts hidden. It only appears after the user picks an answer.
  • Save index.html.
  • Your browser auto-refreshes. You should now see "Question 1 of 5", "Score: 0", and a "Next Question" button below the start screen content.

Don't see the new quiz elements?

  • Confirm you pasted the quiz-screen code after the closing </div> of start-screen, not inside it.
  • Make sure you did not accidentally delete the closing </div> of quiz-container. It should still wrap both screens.

Help me fix my HTML structure for the quiz screen.

Add the results screen

The final screen congratulates the user and shows their score. It also includes a "Play Again" button that resets the quiz.

  • Find the closing </div> tag of the quiz-screen section (the one right after the next-btn button).
  • Add the following code directly below it, still inside quiz-container:
        <div id="results-screen" class="hidden">
            <h2>Quiz Complete!</h2>
            <p id="final-score"></p>
            <p id="result-message"></p>
            <button id="restart-btn">Play Again</button>
        </div>

What does this code do?

  • The final-score and result-message paragraphs are empty. JavaScript will fill them with the score and a performance message when the quiz ends.
  • The restart-btn button gives users a way to play again without refreshing the page.
  • Like the quiz screen, this screen starts with class="hidden" so it only appears when the quiz is complete.
  • Save index.html.
  • Your browser auto-refreshes. You should see "Quiz Complete!" and a "Play Again" button added at the bottom of the page.

Why is everything showing at once?

All three screens are visible because the .hidden CSS class has no style rules yet. Once you add CSS in the next step, only the start screen will be visible by default.

Seeing a jumbled layout?

  • Check that your three screen divs (start-screen, quiz-screen, results-screen) are siblings inside quiz-container, not nested inside each other.
  • Count your closing </div> tags. You should have one for each screen, one for quiz-container, and one for the progress div inside the quiz screen.

Help me check my HTML structure is correct.

✔️ Awesome, I've got everything!

Great. Double check you have saved index.html and all three screens are visible in the browser.

ⓧ 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</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="quiz-container">
        <div id="start-screen">
            <h1>JavaScript Quiz</h1>
            <p>Test your knowledge with 5 questions!</p>
            <button id="start-btn">Start Quiz</button>
        </div>
        <div id="quiz-screen" class="hidden">
            <div class="progress">
                <span id="question-number">Question 1 of 5</span>
                <span id="score">Score: 0</span>
            </div>
            <h2 id="question"></h2>
            <div id="answers"></div>
            <button id="next-btn" class="hidden">Next Question</button>
        </div>
        <div id="results-screen" class="hidden">
            <h2>Quiz Complete!</h2>
            <p id="final-score"></p>
            <p id="result-message"></p>
            <button id="restart-btn">Play Again</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

Your HTML structure is complete. Next up, you will add CSS to give the page a dark theme, style the buttons, and hide the quiz and results screens until they are needed.

Style the Quiz with CSS

Your HTML structure is ready, but right now it looks like plain text on a white background. CSS transforms that raw structure into a polished, dark-themed interface.

In this step, you will style the quiz container, buttons, and text so the app looks great before you write any JavaScript logic.

In this step, get ready to:
  • Create the dark theme page layout and quiz container.
  • Style the answer buttons with hover and correct/incorrect feedback classes.
  • Add a hidden utility class for showing and hiding screens.
Create the dark theme layout

Every web app needs a visual foundation. You will start by resetting browser defaults, setting the dark background, and creating the centered quiz card.

  • Switch back to style.css in VS Code.
  • Paste in the page layout styles by copying this code:
/* Reset default browser spacing */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

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

/* Main quiz card */
.quiz-container {
    background-color: #16213e;
    border-radius: 16px;
    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 resets all elements so every browser starts from the same baseline.
  • The body uses flexbox to center everything vertically and horizontally on a deep navy background.
  • The .quiz-container creates a rounded card with a subtle shadow that floats in the center of the page.
  • Save style.css.
  • Right-click index.html in the VS Code Explorer sidebar and select Open with Live Server.
  • Your browser opens with a dark navy background and a slightly lighter card in the center.

What is Live Server?

Live Server is a VS Code extension that runs a local web server and automatically reloads your browser every time you save a file. From now on, every time you save, your browser updates instantly.

Don't see the dark background?

  • Confirm your index.html has <link rel="stylesheet" href="style.css"> in the <head> section.
  • Make sure style.css is in the same folder as index.html.
  • Check for typos in the filename. It must be exactly style.css (lowercase).

Why isn't my CSS loading in the browser?

Next, add the styles for headings, the progress bar, and the answers container.

  • Add the following code below your .quiz-container rule in style.css:
/* Heading sizes */
h1 {
    font-size: 2rem;
    margin-bottom: 16px;
    text-align: center;
}

h2 {
    font-size: 1.4rem;
    margin-bottom: 24px;
    line-height: 1.4;
}

/* Question number and score row */
.progress {
    display: flex;
    justify-content: space-between;
    margin-bottom: 24px;
    font-size: 0.9rem;
    color: #aaa;
}

/* Vertical stack for answer buttons */
#answers {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 24px;
}

What does this code do?

  • The h1 and h2 rules set appropriate sizes for the quiz title and question text.
  • The .progress row uses justify-content: space-between to push the question number to the left and the score to the right.
  • The #answers container stacks answer buttons vertically with a 12px gap between each.
  • Save style.css and check your browser.
  • The "JavaScript Quiz" heading is now larger and centered. The subtitle text sits neatly below it.

Now add the styles for the action buttons and screen text.

  • Add this code below the #answers rule in style.css:
/* Pink action buttons (Start, Next, Restart) */
button#start-btn,
button#next-btn,
button#restart-btn {
    background-color: #e94560;
    color: white;
    border: none;
    border-radius: 8px;
    padding: 14px 32px;
    font-size: 1.1rem;
    cursor: pointer;
    display: block;
    margin: 0 auto;
    transition: background-color 0.2s;
}

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

/* Center text on start and results screens */
#start-screen,
#results-screen {
    text-align: center;
}

#start-screen p,
#results-screen p {
    margin-bottom: 24px;
    color: #aaa;
    font-size: 1.1rem;
}

/* Final score highlight */
#final-score {
    font-size: 1.8rem;
    color: #e94560;
    font-weight: bold;
}

#result-message {
    color: #aaa;
}

What does this code do?

  • The button#start-btn selector (and its siblings) styles all three action buttons with a pink background and smooth hover transition.
  • The #start-screen and #results-screen rules center all their text content.
  • The #final-score rule makes the score number large and pink so it stands out on the results screen.
  • Save style.css and check your browser.
  • The "Start Quiz" button is now a bold pink rectangle. All the text on the page is centered.

Button still looks plain?

  • Make sure you typed button#start-btn (no space between button and #start-btn).
  • Confirm there are no missing semicolons or curly braces above this section.

Why isn't my CSS button styling showing up?

Style the answer buttons

Answer buttons are created dynamically by JavaScript, so you will not see them yet. These styles prepare for the three visual states each button can have: default, correct, and incorrect.

  • Add this code below the #result-message rule in style.css:
/* Answer button base style */
.answer-btn {
    background-color: #0f3460;
    color: #eee;
    border: 2px solid #0f3460;
    border-radius: 8px;
    padding: 14px 20px;
    font-size: 1rem;
    cursor: pointer;
    text-align: left;
    transition: background-color 0.2s, border-color 0.2s;
}

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

/* Green highlight for correct answers */
.answer-btn.correct {
    background-color: #1b7a3d;
    border-color: #2ecc71;
}

/* Red highlight for incorrect answers */
.answer-btn.incorrect {
    background-color: #7a1b1b;
    border-color: #e74c3c;
}

/* Prevent clicking after an answer is selected */
.answer-btn.disabled {
    pointer-events: none;
}

What does this code do?

  • The base .answer-btn style gives each button a dark blue look with smooth hover transitions.
  • The .correct class turns a button green when the learner picks the right answer.
  • The .incorrect class turns a button red for wrong answers.
  • The .disabled class uses pointer-events: none to block all clicks after an answer is chosen.
  • Save style.css.
  • You will not see a change yet. The answer buttons are created by JavaScript in the next step.
Add the hidden utility class

Your HTML already has a hidden class on the quiz and results screens. This single CSS rule makes those elements disappear until JavaScript removes the class.

  • Add this final rule at the bottom of style.css:
/* Utility class to hide screens */
.hidden {
    display: none;
}

Why use a utility class?

Instead of writing separate hiding logic for each screen, a single .hidden class lets your JavaScript show or hide any element by adding or removing one class name. This pattern keeps your code simple and reusable.

  • Save style.css and check your browser.
  • The quiz and results screens disappear. You should only see the "JavaScript Quiz" heading, the subtitle, and the pink "Start Quiz" button.

Still seeing all three screens?

  • Open index.html and confirm that quiz-screen and results-screen both have class="hidden" on their wrapping <div> elements.
  • Make sure there is no typo in .hidden in your CSS. The dot prefix is required for class selectors.

Why are my hidden elements still visible on the page?

✔️ Awesome, I've got everything!

Great. Double check you have saved style.css and your browser only shows the start screen.

ⓧ I'd like to double check the full code

Here is the complete style.css file at this point:

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

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

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

h1 {
    font-size: 2rem;
    margin-bottom: 16px;
    text-align: center;
}

h2 {
    font-size: 1.4rem;
    margin-bottom: 24px;
    line-height: 1.4;
}

.progress {
    display: flex;
    justify-content: space-between;
    margin-bottom: 24px;
    font-size: 0.9rem;
    color: #aaa;
}

#answers {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-bottom: 24px;
}

.answer-btn {
    background-color: #0f3460;
    color: #eee;
    border: 2px solid #0f3460;
    border-radius: 8px;
    padding: 14px 20px;
    font-size: 1rem;
    cursor: pointer;
    text-align: left;
    transition: background-color 0.2s, border-color 0.2s;
}

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

.answer-btn.correct {
    background-color: #1b7a3d;
    border-color: #2ecc71;
}

.answer-btn.incorrect {
    background-color: #7a1b1b;
    border-color: #e74c3c;
}

.answer-btn.disabled {
    pointer-events: none;
}

button#start-btn,
button#next-btn,
button#restart-btn {
    background-color: #e94560;
    color: white;
    border: none;
    border-radius: 8px;
    padding: 14px 32px;
    font-size: 1.1rem;
    cursor: pointer;
    display: block;
    margin: 0 auto;
    transition: background-color 0.2s;
}

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

#start-screen,
#results-screen {
    text-align: center;
}

#start-screen p,
#results-screen p {
    margin-bottom: 24px;
    color: #aaa;
    font-size: 1.1rem;
}

#final-score {
    font-size: 1.8rem;
    color: #e94560;
    font-weight: bold;
}

#result-message {
    color: #aaa;
}

.hidden {
    display: none;
}

Your quiz looks polished and professional. Next up, you will write the JavaScript that brings this interface to life by loading questions and responding to clicks.

Write the Quiz Logic in JavaScript

Your HTML structure and CSS styling are ready. Now you need the brains of the operation.

JavaScript is what turns a static web page into an interactive experience. In this step, you'll set up the quiz data, connect your JavaScript to the HTML elements on the page, and wire up the buttons so they actually do something when clicked.

In this step, get ready to:
  • Create the quiz questions as a JavaScript data structure.
  • Select HTML elements and store them in variables for easy access.
  • Add event listeners that respond to button clicks.
Create the questions array

Every quiz needs questions. You'll store them in an array of objects, where each object holds the question text, the answer options, and which answer is correct.

  • In VS Code, click on script.js in the Explorer sidebar to open it.
  • Add the questions array by pasting the following code:
// Quiz questions stored as an array of objects
const questions = [
    {
        question: "What keyword is used to declare a variable that cannot be reassigned?",
        answers: ["var", "let", "const", "fixed"],
        correct: 2
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: [
            "document.querySelector()",
            "document.getElementById()",
            "document.getElement()",
            "document.findById()"
        ],
        correct: 1
    },
    {
        question: "What does === check in JavaScript?",
        answers: [
            "Value only",
            "Type only",
            "Value and type",
            "Reference only"
        ],
        correct: 2
    },
    {
        question: "Which array method creates a new array by transforming each element?",
        answers: ["filter()", "forEach()", "map()", "reduce()"],
        correct: 2
    },
    {
        question: "What event fires when a user clicks a button?",
        answers: ["onhover", "click", "submit", "keydown"],
        correct: 1
    }
];

What does this code do?

Each object in the questions array has three properties:

  • question holds the question text displayed to the user.
  • answers is an array of four possible answer strings.
  • correct stores the index (starting at 0) of the correct answer in the answers array.
  • Save script.js.

You won't see a change in the browser yet. The next chunk connects this data to the page.

Select DOM elements and store them in variables

To change what appears on the page, JavaScript needs a way to "grab" each HTML element. The DOM (Document Object Model) is the browser's representation of your HTML as objects that JavaScript can read and modify.

  • Add the following code below the questions array in script.js:
// State variables to track quiz progress
let currentQuestion = 0;
let score = 0;

// Select all DOM elements we need to show, hide, or update
const startScreen = document.getElementById("start-screen");
const quizScreen = document.getElementById("quiz-screen");
const resultsScreen = document.getElementById("results-screen");
const startBtn = document.getElementById("start-btn");
const nextBtn = document.getElementById("next-btn");
const restartBtn = document.getElementById("restart-btn");
const questionEl = document.getElementById("question");
const answersEl = document.getElementById("answers");
const questionNumber = document.getElementById("question-number");
const scoreEl = document.getElementById("score");
const finalScore = document.getElementById("final-score");
const resultMessage = document.getElementById("result-message");

What does this code do?

The two let variables track state. currentQuestion remembers which question the user is on. score counts how many they got right. Both use let because their values will change as the quiz progresses.

Each document.getElementById() call finds the HTML element with that ID and stores a reference to it. This way, you only look up each element once instead of searching the page every time you need to change something.

Why const for DOM references?

The variables use const because the reference to each element never changes. You'll modify what's inside the elements (their text, classes, children), but the variable always points to the same element.

  • Save script.js.

You won't see a visual change yet. The next chunk uses these references to make the buttons respond to clicks.

Add event listeners to the buttons

Event listeners tell JavaScript to run a function whenever the user does something, like clicking a button. You need three listeners: one to start the quiz, one to advance to the next question, and one to restart.

  • Add the following code below your DOM references in script.js:
// Attach click handlers to each button
startBtn.addEventListener("click", startQuiz);
nextBtn.addEventListener("click", showNextQuestion);
restartBtn.addEventListener("click", restartQuiz);

// Hide start screen and show quiz screen
function startQuiz() {
    startScreen.classList.add("hidden");
    quizScreen.classList.remove("hidden");
    showQuestion();
}

// Move to the next question
function showNextQuestion() {
    currentQuestion++;
    showQuestion();
}

// Reset everything and go back to start
function restartQuiz() {
    currentQuestion = 0;
    score = 0;
    resultsScreen.classList.add("hidden");
    startScreen.classList.remove("hidden");
}

What does this code do?

  • addEventListener("click", functionName) tells the browser: "When this button is clicked, run this function."
  • startQuiz() hides the start screen and reveals the quiz screen by toggling the hidden CSS class.
  • showNextQuestion() increments the question counter by one and calls showQuestion() (which you'll build in the next step).
  • restartQuiz() resets score and question number back to zero, then swaps back to the start screen.
  • Save script.js.
  • Switch to your browser and click Start Quiz. The start screen disappears and you see the quiz screen with the progress bar (but no question text yet).

Why is the quiz screen empty?

The startQuiz function calls showQuestion(), which doesn't exist yet. You'll see an error in the browser console. That's expected. You'll build showQuestion in the next step.

Start Quiz button not doing anything?

  • Make sure you saved script.js. Unsaved changes won't run in the browser.
  • Check that your index.html has <script src="script.js"></script> right before the closing </body> tag.
  • Confirm the button ID in your HTML is start-btn (with a hyphen, not an underscore).

Help me debug why my Start Quiz button isn't working.

✔️ Awesome, I've got everything!

Great. Double check you've saved script.js and move on.

ⓧ I'd like to double check the full code

Here's what your complete script.js should look like at this point:

const questions = [
    {
        question: "What keyword is used to declare a variable that cannot be reassigned?",
        answers: ["var", "let", "const", "fixed"],
        correct: 2
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: [
            "document.querySelector()",
            "document.getElementById()",
            "document.getElement()",
            "document.findById()"
        ],
        correct: 1
    },
    {
        question: "What does === check in JavaScript?",
        answers: [
            "Value only",
            "Type only",
            "Value and type",
            "Reference only"
        ],
        correct: 2
    },
    {
        question: "Which array method creates a new array by transforming each element?",
        answers: ["filter()", "forEach()", "map()", "reduce()"],
        correct: 2
    },
    {
        question: "What event fires when a user clicks a button?",
        answers: ["onhover", "click", "submit", "keydown"],
        correct: 1
    }
];

let currentQuestion = 0;
let score = 0;

const startScreen = document.getElementById("start-screen");
const quizScreen = document.getElementById("quiz-screen");
const resultsScreen = document.getElementById("results-screen");
const startBtn = document.getElementById("start-btn");
const nextBtn = document.getElementById("next-btn");
const restartBtn = document.getElementById("restart-btn");
const questionEl = document.getElementById("question");
const answersEl = document.getElementById("answers");
const questionNumber = document.getElementById("question-number");
const scoreEl = document.getElementById("score");
const finalScore = document.getElementById("final-score");
const resultMessage = document.getElementById("result-message");

startBtn.addEventListener("click", startQuiz);
nextBtn.addEventListener("click", showNextQuestion);
restartBtn.addEventListener("click", restartQuiz);

function startQuiz() {
    startScreen.classList.add("hidden");
    quizScreen.classList.remove("hidden");
    showQuestion();
}

function showNextQuestion() {
    currentQuestion++;
    showQuestion();
}

function restartQuiz() {
    currentQuestion = 0;
    score = 0;
    resultsScreen.classList.add("hidden");
    startScreen.classList.remove("hidden");
}

Your quiz now has its data, knows where every element lives on the page, and responds to button clicks. Next up, you'll build the functions that actually load questions and check answers.

Handle Answer Selection and Scoring

In the last step, you set up the core JavaScript structure. Your quiz has variables tracking state, DOM references for every element, and event listeners ready to fire.

Now it's time to make those event listeners actually do something. You'll write the three functions that power the entire quiz experience: showing questions, handling answers, and displaying results.

In this step, get ready to:
  • Build the showQuestion function that dynamically creates answer buttons.
  • Build the selectAnswer function that checks answers and updates the score.
  • Build the showResults function that displays the final score.
Build the showQuestion function

When the quiz starts, it needs to display the current question text and generate a clickable button for each possible answer. The showQuestion function handles all of this by reading from the questions array and creating fresh buttons each time.

  • In script.js, find the closing } of the startQuiz function.
  • Add the following showQuestion function below it, above showNextQuestion:
function showQuestion() {
    // Get the current question object from the array
    const current = questions[currentQuestion];
    questionNumber.textContent = `Question ${currentQuestion + 1} of ${questions.length}`;
    scoreEl.textContent = `Score: ${score}`;
    questionEl.textContent = current.question;
    answersEl.innerHTML = "";

    // Create a button for each answer option
    current.answers.forEach(function (answer, index) {
        const button = document.createElement("button");
        button.textContent = answer;
        button.classList.add("answer-btn");
        button.addEventListener("click", function () {
            selectAnswer(index);
        });
        answersEl.appendChild(button);
    });

    // Hide the Next button until an answer is selected
    nextBtn.classList.add("hidden");
}

What does this code do?

This function reads the current question from the questions array and updates the page with its text and answers.

  • answersEl.innerHTML = "" clears any buttons from the previous question so you start fresh.
  • document.createElement("button") builds a new button element for each answer choice.
  • Each button gets a click listener that passes its index to selectAnswer so the quiz knows which option was clicked.

What is createElement?

document.createElement() creates a new HTML element that you can configure in JavaScript before adding it to the page. It's how you build DOM elements on the fly instead of hardcoding them in your HTML.

  • Save script.js.
  • In your browser, click Start Quiz. You should see the first question ("What keyword is used to declare a variable that cannot be reassigned?") with four answer buttons below it.

Don't see the question or buttons?

  • Check that your showQuestion function is placed after startQuiz and before showNextQuestion. The function name must match exactly.
  • Open DevTools with Cmd+Option+I (macOS) or Ctrl+Shift+I (Windows) and check the Console tab for red error messages.

Help me debug why my showQuestion function isn't displaying the quiz question.

Build the selectAnswer function

Right now you can see questions, but clicking an answer does nothing. The selectAnswer function will check whether the user picked correctly, give visual feedback, and update the score.

  • Below the showQuestion function you just added, paste the following selectAnswer function (still above showNextQuestion):
function selectAnswer(selected) {
    const current = questions[currentQuestion];
    const buttons = answersEl.querySelectorAll(".answer-btn");

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

    // Check if the selected answer matches the correct index
    if (selected === current.correct) {
        buttons[selected].classList.add("correct");
        score++;
        scoreEl.textContent = `Score: ${score}`;
    } else {
        buttons[selected].classList.add("incorrect");
        buttons[current.correct].classList.add("correct");
    }

    // Show Next button or move to results if this was the last question
    if (currentQuestion < questions.length - 1) {
        nextBtn.classList.remove("hidden");
    } else {
        setTimeout(showResults, 1500);
    }
}

What does this code do?

  • querySelectorAll(".answer-btn") grabs all the answer buttons so you can disable them after one is clicked.
  • Adding the disabled class uses your CSS rule (pointer-events: none) to prevent double-clicking.
  • The correct and incorrect classes trigger the green and red styles you defined in style.css.
  • setTimeout(showResults, 1500) waits 1.5 seconds after the final answer before showing results. This gives the user time to see the feedback.
  • Save script.js and return to your browser.
  • Click Start Quiz, then click any answer.
  • You should see the correct answer turn green. If you picked wrong, your choice turns red and the correct one turns green. The Next Question button appears below.

No color change when you click an answer?

  • Make sure the selectAnswer function is placed between showQuestion and showNextQuestion. The spelling must match exactly.
  • Confirm your style.css has the .answer-btn.correct and .answer-btn.incorrect rules from the previous step.

Help me figure out why clicking an answer doesn't change its color.

Build the showResults function

After the final question, the quiz needs to transition to a results screen. The showResults function hides the quiz, displays the score, and gives a performance message based on how many questions were answered correctly.

  • Find the showNextQuestion function. Add the following showResults function below it, above restartQuiz:
function showResults() {
    // Switch from quiz screen to results screen
    quizScreen.classList.add("hidden");
    resultsScreen.classList.remove("hidden");
    finalScore.textContent = `${score} / ${questions.length}`;

    // Show a different message based on performance
    if (score === questions.length) {
        resultMessage.textContent = "Perfect score! You're a JavaScript expert!";
    } else if (score >= questions.length * 0.6) {
        resultMessage.textContent = "Great job! You know your JavaScript well.";
    } else {
        resultMessage.textContent = "Keep practicing! You'll get there.";
    }
}

What does this code do?

  • classList.add("hidden") and classList.remove("hidden") swap which screen is visible. Your .hidden CSS class sets display: none on the element.
  • Template literals (the backtick strings with ${...}) let you embed JavaScript values directly inside text.
  • The conditionals check whether the user scored perfectly, above 60%, or below 60%, and set a matching message.
  • Save script.js and return to your browser.
  • Click Start Quiz and answer all 5 questions.
  • After the last answer, you should see the results screen appear with your score (e.g. "3 / 5") and a performance message.
  • Click Play Again to confirm the quiz resets back to the start screen.

Results screen not appearing after the last question?

  • Make sure showResults is placed between showNextQuestion and restartQuiz. JavaScript must be able to find the function name when setTimeout calls it.
  • Wait 1.5 seconds after clicking your final answer. The results appear on a short delay.
  • Check the Console tab in DevTools for any red error messages pointing to a typo or missing variable.

Help me debug why the results screen isn't showing after the last question.

What is template literal syntax?

Template literals use backticks (`,not regular quotes) and ${expression} to embed variables or math directly in a string. For example, `Score: ${score}` produces "Score: 3" when score is 3.

✔️ Awesome, I've got everything!

Great. Double check you've saved script.js and confirmed the full quiz loop works (start, answer, results, restart).

ⓧ I'd like to double check the full code

Here is the complete script.js file. Compare it against yours to find any differences.

const questions = [
    {
        question: "What keyword is used to declare a variable that cannot be reassigned?",
        answers: ["var", "let", "const", "fixed"],
        correct: 2
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: [
            "document.querySelector()",
            "document.getElementById()",
            "document.getElement()",
            "document.findById()"
        ],
        correct: 1
    },
    {
        question: "What does === check in JavaScript?",
        answers: [
            "Value only",
            "Type only",
            "Value and type",
            "Reference only"
        ],
        correct: 2
    },
    {
        question: "Which array method creates a new array by transforming each element?",
        answers: ["filter()", "forEach()", "map()", "reduce()"],
        correct: 2
    },
    {
        question: "What event fires when a user clicks a button?",
        answers: ["onhover", "click", "submit", "keydown"],
        correct: 1
    }
];

let currentQuestion = 0;
let score = 0;

const startScreen = document.getElementById("start-screen");
const quizScreen = document.getElementById("quiz-screen");
const resultsScreen = document.getElementById("results-screen");
const startBtn = document.getElementById("start-btn");
const nextBtn = document.getElementById("next-btn");
const restartBtn = document.getElementById("restart-btn");
const questionEl = document.getElementById("question");
const answersEl = document.getElementById("answers");
const questionNumber = document.getElementById("question-number");
const scoreEl = document.getElementById("score");
const finalScore = document.getElementById("final-score");
const resultMessage = document.getElementById("result-message");

startBtn.addEventListener("click", startQuiz);
nextBtn.addEventListener("click", showNextQuestion);
restartBtn.addEventListener("click", restartQuiz);

function startQuiz() {
    startScreen.classList.add("hidden");
    quizScreen.classList.remove("hidden");
    showQuestion();
}

function showQuestion() {
    const current = questions[currentQuestion];
    questionNumber.textContent = `Question ${currentQuestion + 1} of ${questions.length}`;
    scoreEl.textContent = `Score: ${score}`;
    questionEl.textContent = current.question;
    answersEl.innerHTML = "";

    current.answers.forEach(function (answer, index) {
        const button = document.createElement("button");
        button.textContent = answer;
        button.classList.add("answer-btn");
        button.addEventListener("click", function () {
            selectAnswer(index);
        });
        answersEl.appendChild(button);
    });

    nextBtn.classList.add("hidden");
}

function selectAnswer(selected) {
    const current = questions[currentQuestion];
    const buttons = answersEl.querySelectorAll(".answer-btn");

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

    if (selected === current.correct) {
        buttons[selected].classList.add("correct");
        score++;
        scoreEl.textContent = `Score: ${score}`;
    } else {
        buttons[selected].classList.add("incorrect");
        buttons[current.correct].classList.add("correct");
    }

    if (currentQuestion < questions.length - 1) {
        nextBtn.classList.remove("hidden");
    } else {
        setTimeout(showResults, 1500);
    }
}

function showNextQuestion() {
    currentQuestion++;
    showQuestion();
}

function showResults() {
    quizScreen.classList.add("hidden");
    resultsScreen.classList.remove("hidden");
    finalScore.textContent = `${score} / ${questions.length}`;

    if (score === questions.length) {
        resultMessage.textContent = "Perfect score! You're a JavaScript expert!";
    } else if (score >= questions.length * 0.6) {
        resultMessage.textContent = "Great job! You know your JavaScript well.";
    } else {
        resultMessage.textContent = "Keep practicing! You'll get there.";
    }
}

function restartQuiz() {
    currentQuestion = 0;
    score = 0;
    resultsScreen.classList.add("hidden");
    startScreen.classList.remove("hidden");
}

Secret mission

Add a Countdown Timer

Your quiz works great, but there's no time pressure. Real quizzes keep you on your toes with a ticking clock. Can you add a 10-second countdown timer that auto-advances when time runs out?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • Project folder on your Desktop (contains index.html, style.css, script.js).
  • Live Server extension (by Ritwick Dey) in VS Code.
  • Visual Studio Code.

Keep everything running

No action needed. Choose this if you're still actively building or plan to customize the quiz with your own questions.

  • Your project folder stays on your Desktop, ready to open any time.
  • Visual Studio Code and the Live Server extension remain installed for future web projects.

Pause - I'll come back to this later

Stop Live Server to free up the browser tab and local port. Your files stay safe on disk.

  • In VS Code, click the Port: 5500 indicator in the bottom status bar to stop Live Server.
  • Close the browser tab that was serving your quiz.
  • Close VS Code when you are done.

Delete - I don't want to use this again

Remove all project files and optionally uninstall the tools you installed during this project.

  • Delete the project folder from your Desktop.

On macOS or Linux, run:

rm -rf ~/Desktop/javascript-quiz

On Windows, run:

rmdir /s /q "$HOME\Desktop\javascript-quiz"
  • Uninstall the Live Server extension by opening VS Code, clicking the Extensions icon in the left sidebar, finding Live Server in the list, and clicking Uninstall.
  • Uninstall Visual Studio Code through your operating system's standard app removal process.

Nice Work!

Nice Work!

Nice work! You just built a fully interactive quiz app from scratch using nothing but HTML, CSS, and vanilla JavaScript.

You've learned how to:

  • Manipulated the DOM dynamically using createElement, classList, and textContent to build a multi-screen quiz interface.
  • Handled user interaction with addEventListener and click events to respond to answer selections.
  • Managed application state using JavaScript variables to track the current question and score across screens.
  • Added a countdown timer using setInterval and clearInterval that auto-advances when time runs out.

Ready to quiz yourself?