OnlineCBT Logo OnlineCBT
JavaScript Tutorial
JAVASCRIPT · LESSON 35

JS Project: Interactive Quiz App

By Ratan Updated: 16 Oct 2026

Build a dynamic multiple-choice quiz app with score tracking and DOM updates.

Level: Beginner to Advanced
Duration: ~60 Mins Deep-Dive

Build a quiz with one score update per question

This project combines arrays, DOM creation, form submission and explicit UI state. A question has text, answer options, one correct index and an explanation. The score changes only after a valid first submission; Next advances without scoring again.

HTML, inside the body

JavaScript Practice Quiz

quiz.js

const questions = [
  {
    text: 'Which declaration prevents reassignment of its binding?',
    options: ['let', 'const', 'var'],
    answer: 1,
    explanation: 'const prevents rebinding; an object it references may still mutate.'
  },
  {
    text: 'Which property safely inserts plain user text?',
    options: ['innerHTML', 'outerHTML', 'textContent'],
    answer: 2,
    explanation: 'textContent treats the value as text instead of parsing HTML.'
  },
  {
    text: 'What does [2, 3].map(value => value * 2) produce?',
    options: ['[4, 6]', '5', '[2, 3]'],
    answer: 0,
    explanation: 'map returns a new array with one transformed result per visited element.'
  }
];
const form = document.querySelector('#quiz');
const legend = document.querySelector('#question');
const options = document.querySelector('#options');
const progress = document.querySelector('#progress');
const feedback = document.querySelector('#feedback');
const check = document.querySelector('#check');
const next = document.querySelector('#next');
const restart = document.querySelector('#restart');
let index = 0;
let score = 0;
let answered = false;

function render() {
  answered = false;
  feedback.textContent = '';
  options.replaceChildren();
  if (index === questions.length) {
    form.hidden = true;
    restart.hidden = false;
    progress.textContent = 'Quiz complete';
    feedback.textContent = 'Score: ' + score + ' / ' + questions.length;
    restart.focus();
    return;
  }
  form.hidden = false;
  restart.hidden = true;
  check.disabled = false;
  next.hidden = true;
  const item = questions[index];
  legend.textContent = item.text;
  progress.textContent = 'Question ' + (index + 1) + ' of ' + questions.length;
  item.options.forEach((text, optionIndex) => {
    const row = document.createElement('p');
    const label = document.createElement('label');
    const input = document.createElement('input');
    input.type = 'radio';
    input.name = 'answer';
    input.value = String(optionIndex);
    input.required = true;
    label.append(input, ' ', text);
    row.append(label);
    options.append(row);
  });
}
form.addEventListener('submit', event => {
  event.preventDefault();
  if (answered) return;
  const selected = form.querySelector('input[name="answer"]:checked');
  if (!selected) {
    feedback.textContent = 'Select one answer first.';
    return;
  }
  const item = questions[index];
  const correct = Number(selected.value) === item.answer;
  if (correct) score += 1;
  answered = true;
  feedback.textContent = (correct ? 'Correct. ' : 'Incorrect. ')
    + 'Answer: ' + item.options[item.answer] + '. ' + item.explanation;
  form.querySelectorAll('input[name="answer"]').forEach(input => {
    input.disabled = true;
  });
  check.disabled = true;
  next.hidden = false;
  next.textContent = index === questions.length - 1 ? 'Show result' : 'Next';
  next.focus();
});
next.addEventListener('click', () => {
  if (!answered) return;
  index += 1;
  render();
  if (index < questions.length) options.querySelector('input')?.focus();
});
restart.addEventListener('click', () => {
  index = 0;
  score = 0;
  render();
  options.querySelector('input')?.focus();
});
render();

Optional CSS

.tech-lesson-content { font: 1rem/1.6 system-ui, sans-serif; max-width: 42rem; margin: 2rem auto; padding: 1rem; }
fieldset { border: 1px solid #64748b; padding: 1rem; }
button { padding: 0.6rem 1rem; margin-top: 0.75rem; }
:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 3px; }

Test the behavior

  1. Choose const, textContent and [4,6]; expect 3/3.
  2. Choose all incorrect options; expect 0/3 with explanations.
  3. Attempt submission without selecting an option; no score or index should change.
  4. Try repeated answer submission; answered prevents double counting.
  5. Restart after completion; score and question number should reset.
  6. Complete the flow with keyboard only and check the announced feedback.

The answer key is visible in client source. This is a practice quiz, not a secure examination engine. For assessed exams, server-side question delivery, authorization, timing and evaluation require a separate design.

📝 Live Lesson Practice

HTML/CSS JavaScript Python C++ C PHP
⌨️ Practice Inputs (लाइव इनपुट भरें) (खाली होने पर RED, भरने पर GREEN underline)
💻 Code Editor (Monaco VS Code Engine)
👀 Live Preview
Address Contact

+91 7877547686

E-mail

onlinecbtportal@gmail.com

Helpline Number

+91 7877547686


Click To Download
Get it on Google Play

ऐप डाउनलोड करने
के लिए Google Play पर
उपलब्ध है

WhatsApp Chat