Project 1: Interactive Quiz App

📖 Concept

Build an interactive quiz application that tests JavaScript knowledge. This project practices DOM manipulation, event handling, state management, and conditional logic.

Features:

  • Multiple-choice questions with dynamic rendering
  • Score tracking and progress bar
  • Timer for each question
  • Results summary with correct/wrong breakdown
  • Ability to restart

Concepts practiced: DOM manipulation, Event listeners, Template literals, Arrays, Objects, Conditionals, setTimeout, CSS transitions

💻 Code Example

codeTap to expand ⛶
1// Quiz App — Core Architecture
2
3class QuizApp {
4 constructor(questions, container) {
5 this.questions = this.shuffle(questions);
6 this.container = container;
7 this.currentIndex = 0;
8 this.score = 0;
9 this.answers = [];
10 this.timePerQuestion = 30; // seconds
11 this.timer = null;
12 }
13
14 start() {
15 this.currentIndex = 0;
16 this.score = 0;
17 this.answers = [];
18 this.renderQuestion();
19 }
20
21 renderQuestion() {
22 const q = this.questions[this.currentIndex];
23 const progress = ((this.currentIndex) / this.questions.length * 100);
24
25 this.container.innerHTML = `
26 <div class="quiz-header">
27 <div class="progress-bar">
28 <div class="progress-fill" style="width: ${progress}%"></div>
29 </div>
30 <span class="question-count">
31 Question ${this.currentIndex + 1} of ${this.questions.length}
32 </span>
33 <span class="timer" id="timer">${this.timePerQuestion}s</span>
34 </div>
35 <div class="question">
36 <h2>${q.question}</h2>
37 <div class="options">
38 ${q.options.map((opt, i) => `
39 <button class="option-btn" data-index="${i}">
40 ${opt}
41 </button>
42 `).join("")}
43 </div>
44 </div>
45 `;
46
47 // Event delegation for options
48 this.container.querySelector(".options")
49 .addEventListener("click", (e) => {
50 if (e.target.matches(".option-btn")) {
51 this.handleAnswer(parseInt(e.target.dataset.index));
52 }
53 });
54
55 this.startTimer();
56 }
57
58 handleAnswer(selectedIndex) {
59 clearInterval(this.timer);
60 const q = this.questions[this.currentIndex];
61 const correct = selectedIndex === q.correctIndex;
62 if (correct) this.score++;
63
64 this.answers.push({
65 question: q.question,
66 selected: q.options[selectedIndex],
67 correct: q.options[q.correctIndex],
68 isCorrect: correct
69 });
70
71 // Visual feedback
72 const buttons = this.container.querySelectorAll(".option-btn");
73 buttons[q.correctIndex].classList.add("correct");
74 if (!correct) buttons[selectedIndex].classList.add("wrong");
75 buttons.forEach(btn => btn.disabled = true);
76
77 setTimeout(() => {
78 this.currentIndex++;
79 if (this.currentIndex < this.questions.length) {
80 this.renderQuestion();
81 } else {
82 this.showResults();
83 }
84 }, 1500);
85 }
86
87 startTimer() {
88 let timeLeft = this.timePerQuestion;
89 const timerEl = this.container.querySelector("#timer");
90 this.timer = setInterval(() => {
91 timeLeft--;
92 timerEl.textContent = `${timeLeft}s`;
93 if (timeLeft <= 0) {
94 clearInterval(this.timer);
95 this.handleAnswer(-1); // Time's up
96 }
97 }, 1000);
98 }
99
100 showResults() {
101 const percentage = Math.round((this.score / this.questions.length) * 100);
102 this.container.innerHTML = `
103 <div class="results">
104 <h2>Quiz Complete!</h2>
105 <div class="score-circle">
106 <span>${percentage}%</span>
107 </div>
108 <p>${this.score} / ${this.questions.length} correct</p>
109 <button onclick="quiz.start()">Try Again</button>
110 </div>
111 `;
112 }
113
114 shuffle(arr) {
115 return [...arr].sort(() => Math.random() - 0.5);
116 }
117}
118
119// Sample questions
120const questions = [
121 {
122 question: "What does typeof null return?",
123 options: ['"null"', '"undefined"', '"object"', '"boolean"'],
124 correctIndex: 2
125 },
126 {
127 question: "Which is NOT a falsy value?",
128 options: ['0', '""', '"false"', 'undefined'],
129 correctIndex: 2
130 }
131];

🏋️ Practice Exercise

Build It Yourself:

  1. Create the HTML structure with a container div
  2. Style with CSS (dark theme, animations for correct/wrong answers)
  3. Add a question bank with at least 20 JavaScript questions
  4. Implement the timer with visual countdown
  5. Add local storage to save high scores
  6. Bonus: Add difficulty levels and categories

⚠️ Common Mistakes

  • Not clearing the timer when an answer is selected — causes unexpected behavior

  • Modifying the original questions array (mutation) — always work with a copy

  • Not removing old event listeners when re-rendering — causes duplicate handlers

  • Hardcoding the correct answer index — makes it easy to cheat via DevTools

  • Not handling the edge case when time runs out

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for Project 1: Interactive Quiz App. Login to unlock this feature.