Build an Interactive Quiz App

Use JavaScript to build a dynamic quiz app with scoring and feedback.

Introduction

30 Second Summary

Quizzes are everywhere. From personality tests on social media to certification exams, they feel simple to use. Building one from scratch teaches you how interactive web apps actually work under the hood.

In this project, you will build a fully interactive quiz app using HTML, CSS, and JavaScript. Your app will display questions one at a time, check answers with instant visual feedback, and show a final score.

What You'll Build

A trivia quiz web app that presents JavaScript-themed questions, highlights correct and wrong answers in color, and displays a results screen with the player's score.

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

  • A working quiz app that displays questions, accepts clicks, and shows green or red feedback on each answer.
  • Core JavaScript skills including variables, functions, arrays, objects, and DOM manipulation.
  • An event-driven code structure that responds to user clicks and controls page flow with timers.
  • Secret Mission: Add a countdown timer that gives the player 10 seconds per question.

Are there any prerequisites?

You only need basic familiarity with HTML structure (knowing what tags like <div> and <button> are). No prior JavaScript knowledge is required.

Before We Start

Before you can build anything interactive, you need an environment that lets you write code and instantly see results.

In this step, you will install a browser preview tool, create your project folder, and confirm that your setup auto-refreshes when you save changes.

In this step, get ready to:
  • Install the Live Server extension in VS Code.
  • Create a project folder with three files.
  • Verify that your browser auto-refreshes on save.
Install the Live Server extension

Live Server is a VS Code extension that launches a local development server. Every time you save a file, it automatically refreshes your browser so you can see changes instantly.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type VS Code and press Enter to open it.
  • Open the Extensions panel by pressing Cmd+Shift+X (macOS) or Ctrl+Shift+X (Windows).
  • In the search bar at the top of the panel, type Live Server.
  • Find the result by Ritwick Dey (it has millions of installs).
  • Click Install.

What is Live Server?

Live Server watches your project files for changes. When you save, it tells the browser to reload automatically.

Without it, you would need to manually refresh the browser every single time you edit your code.

After installation, you should see a Go Live button appear in the bottom-right corner of the VS Code status bar.

Don't see the Go Live button?

Make sure you installed the correct extension. The author name should say Ritwick Dey under the extension title.

Try closing and reopening VS Code. Some extensions only activate after a restart.

Help me find the Go Live button after installing Live Server.

Create your project folder

Your quiz app will live in a dedicated folder on your Desktop. This keeps everything organized and easy to find.

  • Open the integrated terminal in VS Code by pressing Ctrl+` (this works on both macOS and Windows).
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder by running this command:
mkdir quiz-app

What do these commands do?

  • cd ~/Desktop changes your terminal location to your Desktop folder.
  • mkdir quiz-app creates a new empty folder called quiz-app.

Now open this folder in VS Code so the Explorer panel shows your project.

  • Click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop, select the quiz-app folder, then click Open.

VS Code may ask if you trust the authors of the files in this folder. Click Yes, I trust the authors to continue.

Now create the three files your quiz app needs.

  • In the Explorer sidebar on the left, hover over the QUIZ-APP heading to reveal action icons.
  • Click the New File icon (a page with a plus sign).
  • Type index.html and press Enter.
  • Repeat this process to create style.css.
  • Repeat one more time to create script.js.
  • Confirm all three files exist by checking the Explorer sidebar. You should see index.html, style.css, and script.js listed under QUIZ-APP.

Don't see all three files?

If a file is missing, click the New File icon again and type the exact filename including the extension (.html, .css, or .js).

Make sure you opened the quiz-app folder itself, not a parent folder. The heading in Explorer should say QUIZ-APP.

Help me create files in VS Code's Explorer panel.

Verify Live Server works

Time to confirm that Live Server is connected and your browser refreshes automatically when you save.

  • Click on index.html in the Explorer sidebar to open it in the editor.
  • Add this test content to the file:
<h1>Hello</h1>
  • Save the file by pressing Cmd+S (macOS) or Ctrl+S (Windows).
  • Right-click index.html in the Explorer panel and select Open with Live Server.

Your default browser should open automatically showing the word "Hello" in large text.

  • Back in VS Code, change the text inside index.html to:
<h1>Quiz App</h1>
  • Save the file again.
  • Check your browser. It should automatically update to show "Quiz App" without you needing to refresh the page.

What just happened?

Live Server detected your file change on save and told the browser to reload. This feedback loop is how you will develop the entire project.

Every time you save, the browser reflects your latest code.

  • Now clear the test content. Select all text in index.html by pressing Cmd+A (macOS) or Ctrl+A (Windows), then press Delete.
  • Save the now-empty file.

Browser didn't open or refresh?

If right-clicking didn't show Open with Live Server, make sure you right-clicked the file in the Explorer sidebar (not inside the editor).

If the browser opened but shows a blank page, make sure you saved index.html before launching Live Server. Unsaved files show a dot next to their name in the tab bar.

Help me get Live Server working in my browser.

Your development environment is ready to go. Next up, you will build the HTML structure and CSS styling for your quiz card.

Build the HTML and CSS

Your development environment is ready to go. You have VS Code, Live Server, and a project folder with three empty files waiting to be filled.

Before writing any JavaScript logic, you need to build the visual structure of the quiz. HTML defines what elements exist on the page, and CSS controls how they look. Think of it as building the stage before the actors arrive.

In this step, get ready to:
  • Build the HTML structure with a quiz screen and a results screen.
  • Style the app with a dark color scheme and interactive button states.
  • Preview the styled quiz card in the browser.
Build the HTML structure

Your quiz needs two screens: one that shows questions while the player is answering, and one that shows the final score when the quiz is over. Only one screen is visible at a time.

  • Open index.html in VS Code and replace any existing content with this HTML:
<!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">
        <!-- Quiz screen: visible while answering questions -->
        <div id="quiz-screen">
            <h1>JavaScript Quiz</h1>
            <div id="progress">Question <span id="current-question">1</span> of <span id="total-questions">5</span></div>
            <div id="question"></div>
            <div id="answers"></div>
            <div id="feedback"></div>
        </div>
        <!-- Results screen: shown after all questions are answered -->
        <div id="results-screen" class="hidden">
            <h1>Quiz Complete!</h1>
            <p id="score-text"></p>
            <button id="restart-btn">Play Again</button>
        </div>
    </div>
    <!-- Load JavaScript at the end so HTML elements exist first -->
    <script src="script.js"></script>
</body>
</html>

What does this code do?

The page has one outer container (.quiz-container) holding two inner screens that swap visibility:

  • quiz-screen holds the heading, progress tracker, question text, answer buttons area, and feedback text.
  • results-screen holds the final score and a "Play Again" button. It starts with a hidden class so it is invisible until the quiz ends.
  • The <script> tag loads script.js at the bottom of the body so all HTML elements exist before the script tries to find them.
  • Save index.html.
  • Check your browser. You should see plain, unstyled text including "JavaScript Quiz", "Question 1 of 5", and a "Play Again" button near the bottom.

Don't see any text in the browser?

  • Make sure Live Server is running. Right-click index.html in the Explorer panel and select Open with Live Server.
  • Check that you saved the file. Unsaved changes show a dot on the file tab in VS Code.

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

✔️ Awesome, I've got everything!

Great. Double check you've saved your file, then move on to styling.

ⓧ I'd like to double check the full code

Here is the complete index.html file. Make sure yours matches exactly:

<!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="quiz-screen">
            <h1>JavaScript Quiz</h1>
            <div id="progress">Question <span id="current-question">1</span> of <span id="total-questions">5</span></div>
            <div id="question"></div>
            <div id="answers"></div>
            <div id="feedback"></div>
        </div>
        <div id="results-screen" class="hidden">
            <h1>Quiz Complete!</h1>
            <p id="score-text"></p>
            <button id="restart-btn">Play Again</button>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>
Style the app with CSS

The HTML structure is in place, but it looks like plain text on a white page. CSS transforms it into a polished dark-themed card with clearly defined sections.

  • Open style.css and add the page layout and quiz content styles:
/* Reset default browser spacing */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

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

/* Card container for the quiz */
.quiz-container {
    background-color: #16213e;
    border-radius: 12px;
    padding: 40px;
    width: 100%;
    max-width: 500px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

/* Quiz heading */
h1 {
    color: #e94560;
    text-align: center;
    margin-bottom: 20px;
}

/* Progress indicator text */
#progress {
    color: #a8a8b3;
    text-align: center;
    margin-bottom: 20px;
    font-size: 14px;
}

/* Question text */
#question {
    color: #ffffff;
    font-size: 20px;
    margin-bottom: 24px;
    line-height: 1.4;
}

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

What does this code do?

  • The * selector resets all default spacing so every browser starts from the same baseline.
  • body uses flexbox to center the quiz card vertically and horizontally on a dark navy background.
  • .quiz-container creates the rounded card with a slightly lighter background and a subtle shadow.
  • #answers stacks the answer buttons vertically with a 12px gap between each one.
  • Save style.css and check your browser.
  • You should see a dark navy background with a centered card. The heading "JavaScript Quiz" appears in red, and the progress text shows in grey.

Page still looks unstyled?

  • Confirm the <link> tag in index.html says href="style.css" with no typos or extra folders in the path.
  • Make sure both files are saved. Check for the dot indicator on each tab in VS Code.

Why isn't my CSS applying to my HTML page?

The layout is set. Now add the styles for the interactive elements below the code you just wrote:

  • Add the button, feedback, and results styles to the bottom of style.css:
/* Answer button base style */
.answer-btn {
    background-color: #0f3460;
    color: #ffffff;
    border: 2px solid #0f3460;
    border-radius: 8px;
    padding: 14px 20px;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.2s, border-color 0.2s;
    text-align: left;
}

/* Hover effect for answer buttons */
.answer-btn:hover {
    background-color: #1a4a7a;
    border-color: #e94560;
}

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

/* Red highlight for wrong answers */
.answer-btn.wrong {
    background-color: #e94560;
    border-color: #e94560;
}

/* Feedback text below answers */
#feedback {
    color: #ffffff;
    text-align: center;
    margin-top: 16px;
    font-size: 16px;
    min-height: 24px;
}

/* Results screen layout */
#results-screen {
    text-align: center;
}

/* Score display */
#score-text {
    color: #ffffff;
    font-size: 24px;
    margin-bottom: 24px;
}

/* Play Again button */
#restart-btn {
    background-color: #e94560;
    color: #ffffff;
    border: none;
    border-radius: 8px;
    padding: 14px 32px;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.2s;
}

#restart-btn:hover {
    background-color: #d63851;
}

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

What do these styles do?

  • .answer-btn creates full-width dark blue buttons that highlight their border on hover.
  • .correct and .wrong are classes that JavaScript will add later to turn buttons green or red after the player answers.
  • .hidden sets display: none so JavaScript can toggle entire screens on and off by adding or removing this class.
  • Save style.css.
  • Check your browser. The "Play Again" button should now appear as a red pill-shaped button, and the results screen should be completely hidden.

Button looks unstyled or results screen is still visible?

  • Make sure the results-screen div in your HTML has class="hidden" on it.
  • Check that you pasted this CSS below your earlier styles, not inside an existing rule. Each selector should start at the left margin.

Help me fix my CSS button and visibility styles.

✔️ Awesome, I've got everything!

Great. Double check you've saved your file, then move on to the final preview.

ⓧ 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;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
}

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

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

#progress {
    color: #a8a8b3;
    text-align: center;
    margin-bottom: 20px;
    font-size: 14px;
}

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

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

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

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

.answer-btn.correct {
    background-color: #1b9e4b;
    border-color: #1b9e4b;
}

.answer-btn.wrong {
    background-color: #e94560;
    border-color: #e94560;
}

#feedback {
    color: #ffffff;
    text-align: center;
    margin-top: 16px;
    font-size: 16px;
    min-height: 24px;
}

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

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

#restart-btn {
    background-color: #e94560;
    color: #ffffff;
    border: none;
    border-radius: 8px;
    padding: 14px 32px;
    font-size: 16px;
    cursor: pointer;
    transition: background-color 0.2s;
}

#restart-btn:hover {
    background-color: #d63851;
}

.hidden {
    display: none;
}
Preview your quiz in the browser

Both files are complete. Time to confirm everything renders correctly together.

  • Save both index.html and style.css if you haven't already.
  • Check your browser with Live Server running.

You should see a dark-themed card centered on the page. The heading "JavaScript Quiz" appears in red. A grey progress line reads "Question 1 of 5". The results screen is not visible. No answer buttons appear yet because those will be created by JavaScript in the next step.

Why are there no answer buttons showing?

The #answers div is empty in the HTML. The answer buttons will be created dynamically by JavaScript code in the next step. This is how interactive apps work. The HTML provides an empty container, and JavaScript fills it with content at runtime.

Your quiz has a face now. Next up, you'll bring it to life with JavaScript by loading questions and displaying them on the page.

Store Quiz Data and Display a Question

Your HTML structure and styling are in place. The browser shows a dark card with a heading and empty containers waiting for content.

Now you need to fill those containers with actual quiz content. JavaScript will store your questions as structured data, then read that data and inject it into the page dynamically.

In this step, get ready to:
  • Define quiz questions as an array of objects.
  • Connect JavaScript to your HTML elements using DOM references.
  • Write a function that displays a question and its answer buttons.
Define the questions array

A quiz needs questions, and each question needs answer choices plus a way to know which answer is correct. In JavaScript, you can store all of this in an array of objects.

  • In VS Code, open script.js from the Explorer sidebar. Add the questions array and tracking variables by pasting this code:
var questions = [
    {
        question: "What keyword is used to declare a variable that can be reassigned?",
        answers: ["const", "let", "var", "Both let and var"],
        correct: 3
    },
    {
        question: "What does DOM stand for?",
        answers: ["Document Object Model", "Data Object Management", "Digital Output Mode", "Document Order Method"],
        correct: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correct: 2
    },
    {
        question: "What symbol is used for strict equality in JavaScript?",
        answers: ["==", "===", "!=", "="],
        correct: 1
    },
    {
        question: "Which event fires when a user clicks a button?",
        answers: ["hover", "submit", "click", "keydown"],
        correct: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

What does this code do?

questions is an array (a list) where each item is an object (a bundle of related data). Each object has three properties: a question string, an answers array of four choices, and a correct number pointing to the index of the right answer.

currentQuestionIndex tracks which question the player is on (starting at 0, the first item). score counts how many correct answers the player has given.

Why does correct use a number instead of the answer text?

Arrays are zero-indexed in JavaScript. The first item is at position 0, the second at 1, and so on. Storing the correct answer as an index number (like 3 for "Both let and var") makes it easy to compare against the player's choice later.

  • Save script.js.

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

Get references to HTML elements

JavaScript needs a way to reach into the HTML and change what the user sees. The DOM (Document Object Model) is the browser's live representation of your HTML. You use document.getElementById() to grab a reference to any element by its id attribute.

  • In script.js, add the following code below the line var score = 0; to store references to each HTML element your quiz needs to update:
var questionElement = document.getElementById("question");
var answersElement = document.getElementById("answers");
var feedbackElement = document.getElementById("feedback");
var currentQuestionSpan = document.getElementById("current-question");
var totalQuestionsSpan = document.getElementById("total-questions");
var quizScreen = document.getElementById("quiz-screen");
var resultsScreen = document.getElementById("results-screen");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

totalQuestionsSpan.textContent = questions.length;

What does this code do?

Each document.getElementById() call finds the HTML element with that id and stores it in a variable. This gives your code a direct handle to update that element later without searching for it again every time.

The last line sets the "total questions" text to the actual length of the questions array. If you ever add more questions, the display updates automatically.

  • Save script.js.

You won't see a visible change in the browser yet. The next chunk uses these references to display your first question.

Write the loadQuestion function

A function is a reusable block of code that runs whenever you call it. The loadQuestion function will read the current question from the array and create answer buttons on the page each time it is called.

  • In script.js, add the following code below the line totalQuestionsSpan.textContent = questions.length; to define the function and call it on page load:
function loadQuestion() {
    // Get the current question object from the array
    var currentQuestion = questions[currentQuestionIndex];
    // Display the question text on the page
    questionElement.textContent = currentQuestion.question;
    // Update the progress indicator (e.g. "Question 1 of 5")
    currentQuestionSpan.textContent = currentQuestionIndex + 1;
    // Clear any previous feedback and answer buttons
    feedbackElement.textContent = "";
    answersElement.innerHTML = "";

    // Loop through each answer and create a clickable button
    currentQuestion.answers.forEach(function (answer, index) {
        var button = document.createElement("button");
        button.textContent = answer;
        button.classList.add("answer-btn");
        button.addEventListener("click", function () {
            selectAnswer(index, button);
        });
        answersElement.appendChild(button);
    });
}

loadQuestion();

What does this code do?

The function first reads the current question from the array using currentQuestionIndex as the position. It then updates the question text and progress indicator on the page.

answersElement.innerHTML = "" wipes any old buttons. The forEach loop creates a new button for each answer, styles it with the answer-btn class, and attaches a click event listener that will call selectAnswer (which you will write in the next step).

The final line loadQuestion(); calls the function immediately so the first question appears as soon as the page loads.

  • Save script.js and check your browser.
  • You should see the first question ("What keyword is used to declare a variable that can be reassigned?") with four answer buttons stacked below it. The progress text reads "Question 1 of 5".

Can I click the buttons?

You can click them, but nothing visible will happen yet. The buttons call selectAnswer which does not exist yet. You will write that function in the next step to check answers and update the score.

Don't see the question or buttons?

  • Make sure you saved script.js. Unsaved changes won't be picked up by Live Server.
  • Open the browser console (right-click the page, select **Inspect**, then click the **Console** tab). If you see a red error, check that your element IDs in JavaScript match the IDs in your HTML exactly.
  • Confirm that index.html includes <script src="script.js"></script> just before the closing </body> tag.

Help me debug why my quiz question isn't displaying in the browser.

✔️ Awesome, I've got everything!

Great. Double check you have saved script.js before moving on.

ⓧ I'd like to double check the full code

Here is the complete script.js file so far. Make sure yours matches exactly:

var questions = [
    {
        question: "What keyword is used to declare a variable that can be reassigned?",
        answers: ["const", "let", "var", "Both let and var"],
        correct: 3
    },
    {
        question: "What does DOM stand for?",
        answers: ["Document Object Model", "Data Object Management", "Digital Output Mode", "Document Order Method"],
        correct: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correct: 2
    },
    {
        question: "What symbol is used for strict equality in JavaScript?",
        answers: ["==", "===", "!=", "="],
        correct: 1
    },
    {
        question: "Which event fires when a user clicks a button?",
        answers: ["hover", "submit", "click", "keydown"],
        correct: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

var questionElement = document.getElementById("question");
var answersElement = document.getElementById("answers");
var feedbackElement = document.getElementById("feedback");
var currentQuestionSpan = document.getElementById("current-question");
var totalQuestionsSpan = document.getElementById("total-questions");
var quizScreen = document.getElementById("quiz-screen");
var resultsScreen = document.getElementById("results-screen");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

totalQuestionsSpan.textContent = questions.length;

function loadQuestion() {
    var currentQuestion = questions[currentQuestionIndex];
    questionElement.textContent = currentQuestion.question;
    currentQuestionSpan.textContent = currentQuestionIndex + 1;
    feedbackElement.textContent = "";
    answersElement.innerHTML = "";

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

loadQuestion();

Your quiz displays a question and answer buttons. Next up, you will wire those buttons so clicking one checks whether the answer is correct, updates the score, and advances to the next question.

Check Answers and Track the Score

Your quiz displays questions and answer buttons, but clicking a button does nothing. The quiz has no way to evaluate the player's choice or respond to it.

In this step, you will write the logic that checks whether an answer is correct, gives instant visual feedback (green or red), and moves on to the next question automatically.

In this step, get ready to:
  • Connect the answer buttons to a function that checks the player's choice.
  • Write answer-checking logic that highlights correct and wrong answers.
  • Advance to the next question automatically after a short delay.
Understand how buttons connect to answers

Your loadQuestion function already attaches a click event listener to each button it creates. This is the code inside your loop that wires up the click behavior:

button.addEventListener("click", function () {
    selectAnswer(index, button);
});

What does this code do?

addEventListener("click", ...) tells the browser to run a function whenever the player clicks that button.

When the click happens, it calls selectAnswer and passes two pieces of information: the index of the chosen answer (0, 1, 2, or 3) and the button element itself. Right now, clicking triggers an error because selectAnswer does not exist yet.

Write the selectAnswer function

The selectAnswer function is the brain of the quiz. It evaluates the player's click, updates the score, and changes button colors to give visual feedback.

  • In script.js, find the closing } of the loadQuestion function.
  • Add the following selectAnswer function below it (above the final loadQuestion(); call):
function selectAnswer(selectedIndex, selectedButton) {
    // Get the current question data
    var currentQuestion = questions[currentQuestionIndex];
    // Get all answer buttons so we can disable them
    var buttons = answersElement.querySelectorAll(".answer-btn");

    // Disable all buttons to prevent double-clicking
    buttons.forEach(function (button) {
        button.style.pointerEvents = "none";
    });

    // Check if the selected answer is correct
    if (selectedIndex === currentQuestion.correct) {
        score++;
        selectedButton.classList.add("correct");
        feedbackElement.textContent = "Correct!";
    } else {
        selectedButton.classList.add("wrong");
        buttons[currentQuestion.correct].classList.add("correct");
        feedbackElement.textContent = "Wrong! The correct answer is highlighted.";
    }
}

What does this code do?

  • querySelectorAll(".answer-btn") grabs all four answer buttons so we can disable them after one is clicked.
  • Setting pointerEvents to "none" on each button makes it ignore all clicks. This prevents the player from clicking multiple answers.
  • The if/else block compares the player's choice (selectedIndex) against the correct answer index. If they match, the button turns green. If not, the clicked button turns red and the correct one turns green.
  • classList.add("correct") and classList.add("wrong") apply the CSS classes you styled earlier. The browser handles the color change instantly.
  • Save script.js.
  • Switch to your browser. Click any answer button. A correct answer should turn green with "Correct!" below. A wrong answer should turn red with the correct button highlighted in green.

Clicking a button does nothing?

  • Open your browser's DevTools console (press F12). If you see a "selectAnswer is not defined" error, make sure your new function is placed after the closing } of loadQuestion and before the final loadQuestion(); call.
  • Check that your function name is spelled exactly selectAnswer (capital A). It must match the name used inside the event listener.
  • help me debug why clicking answer buttons does nothing in my quiz app
Advance to the next question automatically

Right now, the quiz gets stuck after the player clicks an answer. The feedback shows, but the next question never appears. You need to add a short delay and then load the next question.

  • In script.js, find the closing } of the if/else block inside selectAnswer.
  • Add this setTimeout call below that closing brace, but still inside the selectAnswer function:
    setTimeout(function () {
        currentQuestionIndex++;
        if (currentQuestionIndex < questions.length) {
            loadQuestion();
        } else {
            showResults();
        }
    }, 1500);

What does this code do?

  • setTimeout waits 1500 milliseconds (1.5 seconds) before running the function inside it. This gives the player time to see the green/red feedback before the next question appears.
  • currentQuestionIndex++ moves to the next question in the array.
  • The if check sees whether there are more questions left. If yes, it loads the next one. If no, it calls showResults() (you will write this function in the next step).
  • Save script.js.
  • Switch to your browser and refresh the page. Click an answer. After 1.5 seconds, the next question should appear automatically. The progress text should update to "Question 2 of 5".

What happens after the last question?

If you answer all 5 questions, you will see an error in the console saying showResults is not defined. That is expected. You will create the showResults function in the next step.

Questions not advancing after 1.5 seconds?

  • Make sure the setTimeout call is inside the selectAnswer function (before its final }), not outside it.
  • Check for a missing comma after 1500 is not the issue. The 1500 is the second argument to setTimeout. Make sure the closing }, 1500); is present on the last line of the timeout.
  • help me debug why my quiz is not advancing to the next question

✔️ Awesome, I've got everything!

Great. Double check you have saved script.js and that clicking answers produces green/red feedback followed by automatic advancement.

ⓧ 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?",
        answers: ["const", "let", "var", "Both let and var"],
        correct: 3
    },
    {
        question: "What does DOM stand for?",
        answers: ["Document Object Model", "Data Object Management", "Digital Output Mode", "Document Order Method"],
        correct: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correct: 2
    },
    {
        question: "What symbol is used for strict equality in JavaScript?",
        answers: ["==", "===", "!=", "="],
        correct: 1
    },
    {
        question: "Which event fires when a user clicks a button?",
        answers: ["hover", "submit", "click", "keydown"],
        correct: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

var questionElement = document.getElementById("question");
var answersElement = document.getElementById("answers");
var feedbackElement = document.getElementById("feedback");
var currentQuestionSpan = document.getElementById("current-question");
var totalQuestionsSpan = document.getElementById("total-questions");
var quizScreen = document.getElementById("quiz-screen");
var resultsScreen = document.getElementById("results-screen");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

totalQuestionsSpan.textContent = questions.length;

function loadQuestion() {
    var currentQuestion = questions[currentQuestionIndex];
    questionElement.textContent = currentQuestion.question;
    currentQuestionSpan.textContent = currentQuestionIndex + 1;
    feedbackElement.textContent = "";
    answersElement.innerHTML = "";

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

function selectAnswer(selectedIndex, selectedButton) {
    var currentQuestion = questions[currentQuestionIndex];
    var buttons = answersElement.querySelectorAll(".answer-btn");

    buttons.forEach(function (button) {
        button.style.pointerEvents = "none";
    });

    if (selectedIndex === currentQuestion.correct) {
        score++;
        selectedButton.classList.add("correct");
        feedbackElement.textContent = "Correct!";
    } else {
        selectedButton.classList.add("wrong");
        buttons[currentQuestion.correct].classList.add("correct");
        feedbackElement.textContent = "Wrong! The correct answer is highlighted.";
    }

    setTimeout(function () {
        currentQuestionIndex++;
        if (currentQuestionIndex < questions.length) {
            loadQuestion();
        } else {
            showResults();
        }
    }, 1500);
}

loadQuestion();

Your quiz now gives instant feedback on every answer. Next up, you will display the final score and let the player restart the quiz.

Show Results and Restart

Your quiz now checks answers and tracks the score. But after the last question, the selectAnswer function calls showResults() which does not exist yet.

In this step, you will write the functions that display the final score and let the player restart the quiz from scratch.

In this step, get ready to:
  • Write a function that shows the results screen with the final score.
  • Write a function that resets the quiz so the player can try again.
  • Connect the Play Again button to the restart function.
Write the showResults function

When the player finishes all 5 questions, the quiz screen needs to disappear and a results screen needs to take its place.

The HTML for the results screen already exists in index.html. It is hidden by the hidden class. Your function just needs to toggle that class on and off.

  • In script.js, scroll to the bottom of the file and find the loadQuestion(); call on the last line.
  • Add the following function above loadQuestion();:
function showResults() {
    // Hide the quiz screen
    quizScreen.classList.add("hidden");
    // Show the results screen
    resultsScreen.classList.remove("hidden");
    // Display the final score
    scoreText.textContent = "You scored " + score + " out of " + questions.length + "!";
}

What does this code do?

  • quizScreen.classList.add("hidden") adds the CSS class that sets display: none on the quiz screen, making it disappear.
  • resultsScreen.classList.remove("hidden") removes that same class from the results screen, making it visible.
  • scoreText.textContent builds a string like "You scored 3 out of 5!" by combining the player's score variable with the total number of questions.
  • Save script.js.
  • You won't see a change yet. The showResults function only runs after all 5 questions are answered. The next chunk adds the restart function so you can test both together.
Write the restartQuiz function

Once the player sees their score, they need a way to play again. The restart function resets the game state back to the beginning and loads the first question.

  • In script.js, add this function below showResults (still above loadQuestion(); at the bottom):
function restartQuiz() {
    // Reset tracking variables to starting values
    currentQuestionIndex = 0;
    score = 0;
    // Swap screens back to the quiz
    resultsScreen.classList.add("hidden");
    quizScreen.classList.remove("hidden");
    // Load the first question again
    loadQuestion();
}

What does this code do?

  • currentQuestionIndex = 0 and score = 0 reset both tracking variables so the quiz starts fresh.
  • The class toggling is the reverse of showResults. It hides the results screen and brings back the quiz screen.
  • loadQuestion() re-reads question index 0 from the array, so the player sees the first question again.
  • Save script.js.
Wire up the restart button

The function exists, but the button does not know about it yet. You need to attach a click event listener to the Play Again button so it calls restartQuiz when clicked.

  • In script.js, add this line below the restartQuiz function (still above loadQuestion();):
restartBtn.addEventListener("click", restartQuiz);

What does this line do?

This tells the browser: "When someone clicks the restart button, run the restartQuiz function." It connects your HTML button to your JavaScript logic.

  • Save script.js and switch to your browser.
  • Answer all 5 questions (correct or wrong does not matter). After the last answer, the results screen should appear showing your score (e.g. "You scored 3 out of 5!").
  • Click Play Again. The quiz should restart from Question 1 with the score reset to 0.

Results screen not appearing?

  • Open the browser console (press F12 then click the Console tab) and look for error messages. A red error mentioning showResults is not defined means your function is placed after loadQuestion(); instead of above it.
  • Make sure the function name is exactly showResults (capital R). JavaScript is case-sensitive.
  • If Play Again does nothing, check that the event listener line is outside the restartQuiz function's closing brace, not inside it.

Help me debug why my results screen is not showing after the last question.

✔️ Awesome, I've got everything!

Great. Double check you have saved your file.

ⓧ I'd like to double check the full code

Here is the complete script.js file at this point:

var questions = [
    {
        question: "What keyword is used to declare a variable that can be reassigned?",
        answers: ["const", "let", "var", "Both let and var"],
        correct: 3
    },
    {
        question: "What does DOM stand for?",
        answers: ["Document Object Model", "Data Object Management", "Digital Output Mode", "Document Order Method"],
        correct: 0
    },
    {
        question: "Which method selects an HTML element by its ID?",
        answers: ["document.querySelector()", "document.getElement()", "document.getElementById()", "document.findById()"],
        correct: 2
    },
    {
        question: "What symbol is used for strict equality in JavaScript?",
        answers: ["==", "===", "!=", "="],
        correct: 1
    },
    {
        question: "Which event fires when a user clicks a button?",
        answers: ["hover", "submit", "click", "keydown"],
        correct: 2
    }
];

var currentQuestionIndex = 0;
var score = 0;

var questionElement = document.getElementById("question");
var answersElement = document.getElementById("answers");
var feedbackElement = document.getElementById("feedback");
var currentQuestionSpan = document.getElementById("current-question");
var totalQuestionsSpan = document.getElementById("total-questions");
var quizScreen = document.getElementById("quiz-screen");
var resultsScreen = document.getElementById("results-screen");
var scoreText = document.getElementById("score-text");
var restartBtn = document.getElementById("restart-btn");

totalQuestionsSpan.textContent = questions.length;

function loadQuestion() {
    var currentQuestion = questions[currentQuestionIndex];
    questionElement.textContent = currentQuestion.question;
    currentQuestionSpan.textContent = currentQuestionIndex + 1;
    feedbackElement.textContent = "";
    answersElement.innerHTML = "";

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

function selectAnswer(selectedIndex, selectedButton) {
    var currentQuestion = questions[currentQuestionIndex];
    var buttons = answersElement.querySelectorAll(".answer-btn");

    buttons.forEach(function (button) {
        button.style.pointerEvents = "none";
    });

    if (selectedIndex === currentQuestion.correct) {
        score++;
        selectedButton.classList.add("correct");
        feedbackElement.textContent = "Correct!";
    } else {
        selectedButton.classList.add("wrong");
        buttons[currentQuestion.correct].classList.add("correct");
        feedbackElement.textContent = "Wrong! The correct answer is highlighted.";
    }

    setTimeout(function () {
        currentQuestionIndex++;
        if (currentQuestionIndex < questions.length) {
            loadQuestion();
        } else {
            showResults();
        }
    }, 1500);
}

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

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

restartBtn.addEventListener("click", restartQuiz);

loadQuestion();

Secret mission

Add a Countdown Timer

Your quiz works great, but there is no time pressure. Real quizzes keep you on your toes. Add a 10-second countdown timer to each question 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:

  • The quiz-app project folder on your Desktop (containing index.html, style.css, and script.js).
  • The Live Server VS Code extension.

Keep everything running

No action needed. Choose this if you are still actively building or experimenting with your quiz app.

  • Your quiz-app folder stays on your Desktop ready to open any time.
  • Live Server remains installed in VS Code for future projects.

Pause - I'll come back to this later

Stop the Live Server process to free up the port, but keep all your files for later.

  • In VS Code, click Port: 5500 in the bottom status bar to stop Live Server.
  • Your project files remain on your Desktop. Open the folder again any time to pick up where you left off.

Delete - I don't want to use this again

Remove all project files and the extension to start fresh.

  • Stop Live Server by clicking Port: 5500 in the VS Code status bar.
  • Delete the project folder by running this command in your terminal:

macOS

rm -rf ~/Desktop/quiz-app

Windows

rmdir /s /q %USERPROFILE%\Desktop\quiz-app
  • Uninstall the Live Server extension by opening the Extensions panel (Ctrl+Shift+X), searching for Live Server, and clicking Uninstall.

Nice Work!

Nice Work!

You just built a fully interactive quiz app from scratch using plain HTML, CSS, and JavaScript. Every question, every click, every score update runs on code you wrote yourself.

Here's what you learned:

  • Built a complete quiz app using variables to track state, arrays of objects to store structured data, and functions to organize reusable game logic.
  • Manipulated the DOM to dynamically create elements with document.createElement(), update text with textContent, and respond to user clicks with event listeners.
  • Used conditionals to check answers and branch logic, plus setTimeout to control timing between questions.
  • Secret Mission: Added a countdown timer using setInterval and clearInterval to give each question a 10-second time limit.

Ready to quiz yourself?