Interactive Quiz Application

A dynamic multiple-choice quiz app featuring countdown timer, instant correct/incorrect feedback, score calculation, and review screen.

Difficulty: Beginner Est. Time: 30 mins Vanilla JS (ES6+) Includes HTML, CSS, JS & README (.ZIP)
Live UI Runner

Interactive Quiz Application

live-sandbox://quiz-app

1. Project Overview & Features

The Interactive Quiz Application is an essential frontend project designed to help you bridge the gap between learning JavaScript syntax and building interactive, responsive web applications.

Key Features Implemented:

  • Curated questions with multiple choices and explanations
  • Per-question countdown timer bar
  • Instant visual feedback (Green for correct, Red for wrong)
  • Final score celebration with summary analysis and restart button

2. Step-by-Step Code Walkthrough

A Step 1: Semantic HTML Structure

We structure the interface using clean, semantic HTML elements. This ensures maximum accessibility and provides intuitive hooks for CSS classes and JavaScript query selectors.

HTML (index.html)
<div class="quiz-container">
  <div id="quiz-header" class="quiz-header">
    <div class="progress-bar-bg">
      <div id="progress-bar" class="progress-bar"></div>
    </div>
    <div class="meta-row">
      <span id="question-count">Question 1 of 5</span>
      <span id="timer-badge" class="timer-badge"><i class="fas fa-stopwatch"></i> 15s</span>
    </div>
  </div>

  <div id="question-box" class="question-box">
    <h2 id="question-title">Loading question...</h2>
    <div id="options-grid" class="options-grid"></div>
  </div>

  <div id="feedback-box" class="feedback-box hidden">
    <p id="explanation-text"></p>
    <button id="next-btn" class="btn-next">Next Question <i class="fas fa-arrow-right"></i></button>
  </div>

  <div id="results-screen" class="results-screen hidden">
    <div class="trophy-icon"><i class="fas fa-award"></i></div>
    <h2>Quiz Completed!</h2>
    <p id="score-summary">You scored 4 out of 5</p>
    <button id="restart-btn" class="btn-restart"><i class="fas fa-redo"></i> Play Again</button>
  </div>
</div>

B Step 2: Styling & Responsive CSS

Modern CSS flexbox and grid layouts are used to make the UI look polished, centered, and responsive across desktop, tablet, and mobile screens.

CSS (style.css)
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body {
  background: linear-gradient(135deg, #1e1b4b 0%, #0f172a 100%);
  color: #f8fafc;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
}
.quiz-container {
  background: #1e293b;
  border: 1px solid #334155;
  border-radius: 24px;
  padding: 28px;
  width: 100%;
  max-width: 480px;
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
}
.progress-bar-bg {
  background: #334155;
  height: 8px;
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 16px;
}
.progress-bar {
  background: #6366f1;
  height: 100%;
  width: 20%;
  transition: width 0.3s ease;
}
.meta-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  color: #94a3b8;
  font-size: 0.85rem;
  font-weight: 600;
}
.timer-badge {
  background: rgba(99, 102, 241, 0.2);
  color: #818cf8;
  padding: 4px 10px;
  border-radius: 12px;
}
.question-box h2 {
  font-size: 1.25rem;
  margin: 20px 0;
  line-height: 1.4;
}
.options-grid {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.option-btn {
  background: #334155;
  border: 1px solid #475569;
  color: #f8fafc;
  padding: 14px 18px;
  border-radius: 12px;
  font-size: 0.95rem;
  text-align: left;
  cursor: pointer;
  transition: all 0.2s;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.option-btn:hover:not(:disabled) {
  background: #475569;
  border-color: #6366f1;
}
.option-btn.correct {
  background: #065f46;
  border-color: #10b981;
  color: #a7f3d0;
}
.option-btn.wrong {
  background: #7f1d1d;
  border-color: #ef4444;
  color: #fecaca;
}
.feedback-box {
  margin-top: 18px;
  background: #0f172a;
  padding: 16px;
  border-radius: 12px;
  border: 1px solid #334155;
}
.feedback-box p {
  color: #cbd5e1;
  font-size: 0.9rem;
  margin-bottom: 12px;
}
.btn-next, .btn-restart {
  background: #6366f1;
  color: #ffffff;
  border: none;
  padding: 12px 20px;
  border-radius: 10px;
  font-weight: 600;
  cursor: pointer;
  width: 100%;
  font-size: 0.95rem;
  transition: background 0.2s;
}
.btn-next:hover, .btn-restart:hover {
  background: #4f46e5;
}
.results-screen {
  text-align: center;
  padding: 20px 0;
}
.trophy-icon {
  font-size: 4rem;
  color: #facc15;
  margin-bottom: 12px;
}
.results-screen h2 {
  font-size: 1.6rem;
  margin-bottom: 8px;
}
.results-screen p {
  color: #94a3b8;
  margin-bottom: 24px;
}
.hidden {
  display: none;
}

C Step 3: JavaScript Logic & Event Handling

Here is the complete JavaScript code handling the state, event listeners, mathematical logic, and dynamic DOM rendering:

JavaScript (script.js)
const questions = [
  {
    q: 'Which keyword is used to declare a block-scoped variable in JavaScript?',
    options: ['var', 'let', 'set', 'define'],
    correct: 1,
    explanation: '"let" and "const" create block-scoped variables introduced in ES6.'
  },
  {
    q: 'What is the output of typeof null in JavaScript?',
    options: ['"null"', '"undefined"', '"object"', '"number"'],
    correct: 2,
    explanation: 'Due to a historical bug in JavaScript from its earliest versions, typeof null returns "object".'
  },
  {
    q: 'Which array method adds one or more elements to the end of an array?',
    options: ['push()', 'pop()', 'shift()', 'unshift()'],
    correct: 0,
    explanation: 'array.push() adds items to the end and returns the new length.'
  },
  {
    q: 'What does JSON stand for?',
    options: ['JavaScript Object Notation', 'Java Source Online Network', 'JavaScript Optional Native', 'Joint Standard Object Node'],
    correct: 0,
    explanation: 'JSON stands for JavaScript Object Notation, a lightweight data interchange format.'
  },
  {
    q: 'Which method converts a JSON string into a JavaScript object?',
    options: ['JSON.stringify()', 'JSON.parse()', 'JSON.toObject()', 'JSON.convert()'],
    correct: 1,
    explanation: 'JSON.parse() parses a JSON string and constructs the JavaScript value or object.'
  }
];

let currentIndex = 0;
let score = 0;
let timeLeft = 15;
let timer = null;

const questionTitle = document.getElementById('question-title');
const optionsGrid = document.getElementById('options-grid');
const questionCount = document.getElementById('question-count');
const progressBar = document.getElementById('progress-bar');
const timerBadge = document.getElementById('timer-badge');
const feedbackBox = document.getElementById('feedback-box');
const explanationText = document.getElementById('explanation-text');
const nextBtn = document.getElementById('next-btn');
const resultsScreen = document.getElementById('results-screen');
const quizHeader = document.getElementById('quiz-header');
const questionBox = document.getElementById('question-box');
const scoreSummary = document.getElementById('score-summary');
const restartBtn = document.getElementById('restart-btn');

function startQuestion() {
  clearInterval(timer);
  timeLeft = 15;
  timerBadge.innerHTML = `<i class="fas fa-stopwatch"></i> ${timeLeft}s`;
  feedbackBox.classList.add('hidden');

  const current = questions[currentIndex];
  questionTitle.textContent = current.q;
  questionCount.textContent = `Question ${currentIndex + 1} of ${questions.length}`;
  progressBar.style.width = `${((currentIndex + 1) / questions.length) * 100}%`;

  optionsGrid.innerHTML = '';
  current.options.forEach((opt, idx) => {
    const btn = document.createElement('button');
    btn.className = 'option-btn';
    btn.innerHTML = `<span>${opt}</span><i class="far fa-circle"></i>`;
    btn.addEventListener('click', () => selectAnswer(idx));
    optionsGrid.appendChild(btn);
  });

  timer = setInterval(() => {
    timeLeft--;
    timerBadge.innerHTML = `<i class="fas fa-stopwatch"></i> ${timeLeft}s`;
    if (timeLeft <= 0) {
      clearInterval(timer);
      selectAnswer(-1); // time out
    }
  }, 1000);
}

function selectAnswer(selectedIdx) {
  clearInterval(timer);
  const current = questions[currentIndex];
  const buttons = optionsGrid.querySelectorAll('.option-btn');

  buttons.forEach((btn, idx) => {
    btn.disabled = true;
    if (idx === current.correct) {
      btn.classList.add('correct');
      btn.innerHTML = `<span>${current.options[idx]}</span><i class="fas fa-check-circle"></i>`;
    } else if (idx === selectedIdx) {
      btn.classList.add('wrong');
      btn.innerHTML = `<span>${current.options[idx]}</span><i class="fas fa-times-circle"></i>`;
    }
  });

  if (selectedIdx === current.correct) {
    score++;
  }

  explanationText.textContent = current.explanation;
  feedbackBox.classList.remove('hidden');
}

nextBtn.addEventListener('click', () => {
  currentIndex++;
  if (currentIndex < questions.length) {
    startQuestion();
  } else {
    showResults();
  }
});

function showResults() {
  quizHeader.classList.add('hidden');
  questionBox.classList.add('hidden');
  feedbackBox.classList.add('hidden');
  resultsScreen.classList.remove('hidden');
  scoreSummary.textContent = `You scored ${score} out of ${questions.length} (${Math.round((score / questions.length) * 100)}%)!`;
}

restartBtn.addEventListener('click', () => {
  currentIndex = 0;
  score = 0;
  quizHeader.classList.remove('hidden');
  questionBox.classList.remove('hidden');
  resultsScreen.classList.add('hidden');
  startQuestion();
});

startQuestion();

3. Core JavaScript Concepts You Learned

Array of Objects

Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.

State Flow Management

Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.

Timer Interrupts

Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.

DOM Class Toggling

Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.

Score Calculation

Crucial concept utilized to power the dynamic state, user interactions, and styling of this project.

4. How to Run Locally on Your Computer

  1. Download the project ZIP: Click the Download Project (.ZIP) button at the top or in the live runner to save quiz-app-project.zip.
  2. Extract the archive: Unzip the downloaded file. Inside you will find separate index.html, style.css, script.js, index-standalone.html, and a comprehensive README.md.
  3. Open in any browser: Simply double-click index.html (or index-standalone.html) to open and test immediately in Chrome, Firefox, Safari, or Edge.
  4. Edit in Visual Studio Code: Open the extracted folder in VS Code. Right-click index.html and select "Open with Live Server" to enjoy instant live reloading as you modify HTML, CSS, or JS!

Bonus Challenges for Learners

Ready to level up? Try adding these extra features on your own:

  • Add sound effects or audio feedback for user button clicks.
  • Add custom theme color customizer with CSS variables.
  • Export the results to PDF or copy as a formatted text summary.

Other Beginner Projects You Might Like

Beginner30 mins
Math & Calculators

Interactive Calculator

Build a sleek, fully functional calculator with arithmetic operations, decimal support, backspace, and calculation history.

DOM ManipulationEvent Listenerseval & Math Logic+2
Beginner25 mins
Productivity & UI

Smart To-Do List with LocalStorage

A full-featured task manager with local storage persistence, filtering (All, Active, Completed), task counter, and priority levels.

Safe LocalStorage APIArray Methods (map, filter, unshift)Event Delegation & Keyboard Triggers+2
Beginner20 mins
Timers & Clocks

Digital & Analog Dual Clock

A dynamic dual-mode clock with synchronized analog ticking hands, 12h/24h digital time, date formatting, and light/dark theme switch.

setInterval & requestAnimationFrameDate Object MethodsCSS transform: rotate()+2