⏱1–3 hrs
🎯Intermediate
👤Ages 13+
💰Free cost
📍Indoors

Overview

You're going to build a fully functional quiz app that runs right in your browser. It'll have multiple-choice questions, instant feedback on your answers, a running score tracker, and a final results screen that shows how you did. By the end, you'll understand how JavaScript talks to HTML elements, handles user clicks, and updates the page without refreshing.

This project teaches you the real foundations of interactive web apps—the stuff that powers everything from games to surveys to learning platforms. You'll write vanilla JavaScript (no libraries), which means you're directly controlling the DOM and event handling yourself. It's hands-on, builds fast, and when you're done, you have a tool you can customize and share.

What You'll Learn

  • DOM manipulation: selecting elements with querySelector and querySelectorAll, reading and changing textContent and innerHTML, adding/removing classes
  • Event handling: addEventListener, event.target, click events, detecting user input and responding to it
  • State management: storing quiz progress (current question index, score) in JavaScript variables, updating the DOM when state changes
  • Conditional logic: if/else statements to check answers, show/hide UI elements, and control quiz flow
  • Arrays and loops: storing questions in an array, iterating through them, and dynamically building answer buttons
  • Function design: writing reusable functions to display questions, check answers, and update the score

Background

The DOM (Document Object Model) is how JavaScript sees an HTML page. Every element—every button, div, heading—is an object in the DOM tree that you can grab, read, and change. When you click a button or type in a form, the browser fires an 'event.' Your JavaScript code listens for those events and responds by updating the page. That's the core loop of any interactive web app.

Quiz apps are perfect for learning this because they're visual, they respond immediately to user action, and they require you to manage state (like keeping track of which question you're on and how many you got right). You'll use techniques like addEventListener to catch clicks, querySelector to find elements, textContent to change what users see, and classList to swap CSS classes. These skills apply to building chat apps, games, dashboards, anything interactive.

We're building this with zero frameworks because the goal is to see how the browser actually works, not to hide behind someone else's code. You'll write HTML for structure, CSS for styling (optional but recommended), and JavaScript to bring it to life. By the time you're done, you'll feel confident reaching for vanilla JavaScript or moving to frameworks like React because you understand what's underneath.

Materials

Everything on this list is either free or available at a dollar store. No lab equipment required.

Step-by-Step Procedure

  1. 1

    Set up your HTML skeleton and plan your data

    Open your text editor and create a new file called `quiz.html`. Start with a basic HTML5 template: DOCTYPE, html, head, and body tags. In the head, add a title tag and a style tag (you'll write CSS there). In the body, create a simple structure: a div with id 'quizContainer' that will hold everything, a heading for the quiz title, a div for displaying the current question, a div for the answer buttons, and a div for showing the results screen (hidden at first).

    Before you code any JavaScript, sit down and write your quiz questions as a JavaScript array. Each question should be an object with a `question` property (the text), an `answers` array (the choices), and a `correct` property (the index of the right answer). Example: `{ question: 'What is 2+2?', answers: ['3', '4', '5', '6'], correct: 1 }`. Write at least 5 questions about a topic you care about—science, history, pop culture, coding concepts, whatever. Having this data ready before you code the logic makes everything else faster and clearer.

    💡

    Test your HTML by opening the file in your browser. You should see a blank page or basic text. If you see errors or the page doesn't load, check for typos in your HTML tags.

  2. 2

    Write the CSS to make it look presentable

    In the `<style>` tag in the head, write CSS to make your quiz look clean. Style the quizContainer with a max-width (like 600px), centered on the page using margin auto, padding, and a light background color. Make the question text large and readable. Style the answer buttons: they should be clickable, change appearance on hover, and be laid out in a grid or list. Hide the results screen initially with `display: none;`. Add some basic colors, fonts, and spacing to make it inviting—nothing fancy, just enough to make clicking buttons feel good.

    You can keep it minimal (black text, white background, gray buttons) or add colors and gradients if you want. The goal is that when someone visits your quiz, they immediately understand what to do without any confusion. Test your CSS by refreshing the page and clicking around.

    💡

    Use browser DevTools (right-click > Inspect) to tweak CSS on the fly. Once you find colors and spacing you like, copy them back into your HTML file.

  3. 3

    Write JavaScript to track the quiz state and display the current question

    In a `<script>` tag at the bottom of the body (after your HTML elements), start writing JavaScript. Create variables to track: `currentQuestionIndex` (starts at 0), `score` (starts at 0), and `quizData` (your array of questions). Write a function called `displayQuestion()` that grabs the current question from quizData using currentQuestionIndex, finds the quizContainer div using querySelector, and updates it to show the question text.

    Inside displayQuestion(), also dynamically create buttons for each answer. Loop through the `answers` array of the current question, create a button element for each one, set its textContent to the answer text, and append it to a container. Add a data attribute or class to each button so you can identify which answer it is later. Call displayQuestion() at the end of your script to load the first question. Open the file in your browser and verify that your first question appears with all its answer choices as clickable buttons.

    💡

    Use `console.log()` to print your variables and check that quizData is loaded correctly. Open DevTools (F12) and look at the Console tab to debug.

  4. 4

    Add event listeners to detect answer clicks and check correctness

    Now add an event listener to each answer button. Write a function called `handleAnswerClick(event)` that fires when a button is clicked. Inside, extract which answer the user clicked using event.target. Compare the clicked answer's index to the correct answer index (from your quizData). If it matches, increment the score and show 'Correct!' feedback. If it doesn't match, show 'Wrong!' feedback.

    After showing feedback for about 1-2 seconds, move to the next question. You can use `setTimeout()` to delay the transition. Inside handleAnswerClick, after you show feedback, call `setTimeout()` with a callback that increments currentQuestionIndex and calls displayQuestion() again. Also disable the answer buttons after a click so the user can't click multiple times on the same question. Test this by clicking answers and watching the score update and questions change.

    💡

    If buttons aren't responding, make sure you're using addEventListener correctly and that your querySelectorAll is finding all the buttons. Check the Console for errors.

  5. 5

    Build the results screen and final score display

    Add logic to detect when the quiz is finished. In your displayQuestion() function, check if currentQuestionIndex is greater than or equal to the length of quizData. If it is, call a function `showResults()` instead of displaying another question. Inside showResults(), hide the question container and show a results div (you created this in Step 1). Display a message like 'You scored 4 out of 5!' with the score and total questions. Make the results message personal by adding a comment: if score > (total * 0.8), show 'Excellent!', if score > (total * 0.5), show 'Good try!', otherwise 'Keep practicing!'.

    Add a 'Restart Quiz' button on the results screen. Wire it up with an event listener that resets currentQuestionIndex to 0, score to 0, hides the results screen, and calls displayQuestion() to start over. Test the entire flow by answering all questions and checking the results screen.

    💡

    Keep the results simple and encouraging. The goal is to make users feel motivated to try again, not judged.

  6. 6

    Test, debug, and refine the user experience

    Play through your quiz a few times. Deliberately choose wrong answers, then right answers. Check for edge cases: Does the first question load on page load? Do the buttons disable after clicking? Does the score update correctly? Do you make it to the results screen? Does the restart button work? Use DevTools Console to check for JavaScript errors (red messages).

    Make small improvements: Maybe the buttons need more padding, or the results message is too small, or you want to show the current question number (like '3 of 5') at the top. These tweaks make the app feel polished. Ask a friend or family member to try it and watch where they get confused. Your code doesn't have to be perfect, but the user experience should be smooth. If you find bugs, use console.log() to trace the issue.

    💡

    Browsers are forgiving. You can often test by just hitting refresh after editing your HTML file. No build step needed.

  7. 7

    Extend: Add visual feedback and progress indication

    Enhance the feedback loop. When someone selects a correct answer, highlight the button green (add a class, change the background color). For wrong answers, highlight it red and show the correct answer highlighted in green. This immediate visual feedback feels much better than just a text message. Also add a progress bar or counter at the top that shows 'Question 3 of 5' so users know how far they are in the quiz.

    These additions require modifying your handleAnswerClick function to apply CSS classes conditionally, and updating displayQuestion to show the progress counter. The logic is still simple—you're just adding more visual polish. Users will feel like the app is more responsive and professional.

    💡

    CSS classes are your friend. Define classes like `.correct`, `.incorrect`, `.disabled` in your style tag, then add/remove them in JavaScript using classList.add() and classList.remove().

Expected Results

You'll have a working quiz app in your browser with at least 5 questions. Each question has multiple choice answers. When you click an answer, it reveals whether you got it right or wrong, and moves to the next question. Your score updates in real time. After all questions, you see a final results screen showing how many you got right and a 'Restart' button to play again.

The app lives in a single HTML file with embedded CSS and JavaScript—no server, no build step, just open it in your browser and it works. You can customize the questions, change colors, add more questions, or rearrange the layout. The code is yours to fork, modify, and learn from.

📊

Your results may differ — and that's valuable data, not a mistake. Document what you actually observed, not what you expected.

Going Further

💾

Save scores to local storage and build a leaderboard

Use the browser's localStorage API to save each quiz attempt with the score and timestamp. After someone finishes, save their result. Then build a leaderboard page that reads from localStorage and displays their top 5 scores, sorted highest to lowest. This teaches you how web apps persist data without a server. You'll use JSON.stringify() to save objects and JSON.parse() to load them. It's a realistic skill—localStorage is how many browser games and apps remember your progress.

🎨

Build a question editor UI so you can add questions without editing code

Create a second page or modal in your app where users can add new quiz questions. They type the question text, type four answer options, select which one is correct, and click 'Add Question'. Store the new questions in localStorage alongside the default questions. This teaches form handling, input validation, and dynamic array manipulation. It's also genuinely useful—now anyone can use your quiz app to build their own quiz, which is how real tools work. You'll use form submission events, input validation, and DOM creation to make this work.

🌐

Fetch quiz questions from an API and support multiple quiz categories

Instead of hard-coding questions, use the Fetch API to load questions from a public quiz API (like Open Trivia Database). Let users choose a category before starting. This introduces async/await, error handling, and real-world data fetching. You'll learn how web apps talk to servers, how to handle slow networks, and how to display loading states. You can even filter results by difficulty level. This is how modern apps work—data comes from servers, not from the code itself.

Community Notes

Tips and observations from STEMigoo members who completed this study.

No community notes yet — be the first to share a tip!

💬Notes