JavaScript
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax and Types
-
Section 3: Strings and Numbers in Depth
-
Section 4: Control Flow
-
Section 5: Functions
-
Section 6: Objects and Arrays
-
Section 7: Maps, Sets, and Symbols
-
Section 8: Asynchronous JavaScript
-
Section 9: Object-Oriented and Prototypes
-
Section 10: The DOM
-
Section 11: Browser APIs
-
Section 12: Modern JavaScript (ES2015-ES2025)
-
Section 13: Functional Programming Patterns
-
Section 14: Error Handling and Debugging
-
Section 15: Testing
-
Section 16: Accessibility for JavaScript Developers
-
Section 17: Internationalization and Localization
-
Section 18: Performance
-
Section 19: Node.js Fundamentals
-
Section 20: Regular Expressions
-
Section 21: Design Patterns in JavaScript
-
Section 22: Security Basics
-
Section 23: Data Structures and Algorithms in JavaScript
-
Section 24: Practical Projects
-
Section 25: More Advanced Async Patterns
-
Section 26: More Object and Class Practice
-
Section 27: Working with Dates and Internationalization
-
Section 28: Web Components
-
Section 29: More DOM and Browser Practice
-
Section 30: Build Tooling for Vanilla JavaScript
-
Section 31: More Practice Projects
-
Section 32: Interview and Algorithm Practice
-
Section 33: Error Objects (MDN Reference)
-
Section 34: TypedArrays and Binary Data
-
Section 35: Reflection and Metaprogramming (MDN Reference)
-
Section 36: More Global Functions (MDN Reference)
181: Building a Simple Quiz Application
By now, you've got the hang of manipulating the DOM and handling events. But the real magic happens when you combine those skills to manage state—keeping track of where the user is in a process and reacting to their input. Today, we're going to build a simple JavaScript Trivia Quiz. It's a classic project because it forces you to think about how data (your questions) transforms into a UI (the buttons and text the user sees).
Mapping out our quiz data
Before touching any HTML, I always start with the data. If we hard-code every question into the HTML, we're making a maintenance nightmare for ourselves. Instead, I'll use an array of objects. Each object represents a single question, its options, and the correct answer key.
const quizData = [
{
question: "Which keyword is used to declare a block-scoped variable?",
options: ["var", "let", "set", "define"],
correct: 1 // Index of 'let'
},
{
question: "What is the result of '2' + 2 in JavaScript?",
options: ["4", "22", "NaN", "Error"],
correct: 1 // Index of '22'
},
{
question: "Which method is used to add an element to the end of an array?",
options: ["pop()", "shift()", "push()", "join()"],
correct: 2 // Index of 'push()'
}
];
I'm using the index of the correct answer rather than the string itself. Why? Because if I ever decide to change the wording of an answer, I don't have to hunt through my logic to update the "correct" value.
Getting the question onto the screen
Now we need a way to render this. I'll create a function called loadQuiz. This function needs to know which question we're currently on, so I'll keep a global variable currentQuizIndex. I'll grab the question text and the options array, then inject them into the DOM.
let currentQuizIndex = 0;
let score = 0;
function loadQuiz() {
const currentData = quizData[currentQuizIndex];
document.getElementById('question-text').innerText = currentData.question;
const optionsContainer = document.getElementById('options-container');
optionsContainer.innerHTML = ''; // Clear previous buttons
currentData.options.forEach((option, index) => {
const btn = document.createElement('button');
btn.innerText = option;
btn.onclick = () => handleAnswer(index);
optionsContainer.appendChild(btn);
});
}
Handling the user's choice (and fixing a rendering bug)
Here is where I usually trip up if I'm rushing. I wrote the handleAnswer function to check if the clicked index matched the correct index and then increment the currentQuizIndex. But when I first ran it, the quiz just... stopped. I realized I was incrementing the index but forgot to actually call loadQuiz() again to refresh the screen.
I also realized that if the user reaches the end of the array, quizData[currentQuizIndex] becomes undefined, and the whole app crashes. I need a guard clause to handle the end of the quiz.
function handleAnswer(selectedIndex) {
if (selectedIndex === quizData[currentQuizIndex].correct) {
score++;
}
currentQuizIndex++;
if (currentQuizIndex < quizData.length) {
loadQuiz(); // I forgot this the first time!
} else {
showResults();
}
}
function showResults() {
const container = document.getElementById('quiz-container');
container.innerHTML = `<h2>You scored ${score}/${quizData.length}!</h2>`;
}
Putting the pieces together
To make this actually work, you'll need a basic HTML structure with an ID for the question and a container for the buttons. I prefer using a div for the options container so I can dynamically append buttons via JavaScript. This keeps the HTML clean and the logic centralized in the script.
The beauty of this approach is scalability. If you want to add 50 more questions, you don't touch the functions or the HTML; you just add more objects to your quizData array. That's the power of separating your data from your presentation.
📋 Practical Task
Exercise: Implementing a "Reset Quiz" Feature
Modify the showResults function from the lesson so that it doesn't just show the final score, but also provides a "Try Again" button. When this button is clicked, it should reset the score and currentQuizIndex to 0 and call loadQuiz() to restart the application without requiring the user to refresh the browser page.
There are no comments for now.