<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Quiz</title>
<style>
/* General Body Styles */
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background-color: #f4f4f9;
color: #333;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}
/* Main Quiz Container */
#quiz-container {
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 600px;
padding: 2rem;
box-sizing: border-box;
}
/* Question Header */
#question-header {
display: flex;
justify-content: space-between;
align-items: baseline;
border-bottom: 1px solid #e0e0e0;
padding-bottom: 1rem;
margin-bottom: 1.5rem;
}
#question-text {
margin: 0;
font-size: 1.25rem;
font-weight: 600;
}
#question-counter {
font-size: 0.9rem;
color: #888;
white-space: nowrap;
}
/* Options Container & Styling */
#options-container {
display: flex;
flex-direction: column;
gap: 10px;
}
.option-label {
display: flex;
align-items: center;
width: 100%;
padding: 10px;
border: 1px solid #d3d3d3;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s ease, border-color 0.2s ease;
box-sizing: border-box; /* Ensures padding is included in width */
}
.option-label:hover {
background-color: #f0f8ff;
border-color: #89cff0;
}
/* Hiding the actual radio button */
.option-input {
display: none;
}
/* Custom radio button circle */
.option-label::before {
content: '';
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid #ccc;
margin-right: 15px;
background-color: #fff;
transition: border-color 0.2s ease, background-color 0.2s ease;
flex-shrink: 0; /* Prevents the circle from shrinking */
}
/* Style for the selected state (blue inner circle) */
.option-input:checked + .option-label::before {
background-color: #2196f3;
border-color: #2196f3;
background-clip: content-box; /* Creates inner circle effect */
padding: 3px;
}
/* Feedback Styling */
.option-label.correct {
background-color: #e8f5e9; /* Light green */
border-color: #4caf50; /* Green */
}
.option-label.incorrect {
background-color: #ffebee; /* Light red */
border-color: #f44336; /* Red */
}
/* Correct/Incorrect Icons using pseudo-elements */
.option-label.correct::after,
.option-label.incorrect::after {
font-size: 1.5rem;
margin-left: auto;
}
.option-label.correct::after {
content: '✔';
color: #4caf50;
}
.option-label.incorrect::after {
content: '✖';
color: #f44336;
}
/* Disable clicks after an answer is selected */
#options-container.answered .option-label {
pointer-events: none;
cursor: default;
}
/* Feedback and Explanation Text */
#feedback-container {
margin-top: 1.5rem;
padding: 1rem;
border-radius: 8px;
min-height: 50px;
}
#feedback-text {
font-weight: bold;
font-size: 1.1rem;
margin: 0 0 5px 0;
}
#feedback-text.correct { color: #4caf50; }
#feedback-text.incorrect { color: #f44336; }
#explanation-text {
color: #555;
margin: 0;
}
/* Navigation Buttons */
#navigation-container {
display: flex;
justify-content: space-between;
margin-top: 1.5rem;
border-top: 1px solid #e0e0e0;
padding-top: 1rem;
}
.nav-btn {
background-color: #2196f3;
color: white;
border: none;
padding: 10px 20px;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.2s ease, opacity 0.2s ease;
}
.nav-btn:hover {
background-color: #1976d2;
}
.nav-btn:disabled {
background-color: #a0a0a0;
cursor: not-allowed;
opacity: 0.7;
}
/* Final Score Screen */
#final-score-container {
text-align: center;
}
#final-score-container h2 {
font-size: 2rem;
color: #333;
}
#final-score-container p {
font-size: 1.2rem;
color: #555;
}
</style>
</head>
<body>
<div id="quiz-container">
<!-- This section will be populated by JavaScript -->
<div id="question-header">
<h2 id="question-text"></h2>
<p id="question-counter"></p>
</div>
<div id="options-container"></div>
<div id="feedback-container">
<p id="feedback-text"></p>
<p id="explanation-text"></p>
</div>
<div id="navigation-container">
<button id="prev-btn" class="nav-btn">Previous</button>
<button id="next-btn" class="nav-btn">Next</button>
</div>
</div>
<script>
// --- QUIZ DATA ---
// You can easily edit your questions, options, and explanations here.
// "right: true" marks the correct answer.
const quizData = [
{
question: "What is the capital of France?",
explanation: "Paris is the capital and most populous city of France.",
options: [
{ value: "London", right: false },
{ value: "Berlin", right: false },
{ value: "Paris", right: true },
{ value: "Madrid", right: false }
]
},
{
question: "Which planet is known as the Red Planet?",
explanation: "Mars is often called the 'Red Planet' because of its reddish appearance.",
options: [
{ value: "Mars", right: true },
{ value: "Jupiter", right: false },
{ value: "Venus", right: false },
{ value: "Saturn", right: false }
]
},
{
question: "Who wrote 'To Kill a Mockingbird'?",
explanation: "The novel was written by Harper Lee and published in 1960.",
options: [
{ value: "J.K. Rowling", right: false },
{ value: "Harper Lee", right: true },
{ value: "George Orwell", right: false },
{ value: "F. Scott Fitzgerald", right: false }
]
}
];
// --- DOM ELEMENT REFERENCES ---
const quizContainer = document.getElementById('quiz-container');
const questionTextEl = document.getElementById('question-text');
const questionCounterEl = document.getElementById('question-counter');
const optionsContainerEl = document.getElementById('options-container');
const feedbackTextEl = document.getElementById('feedback-text');
const explanationTextEl = document.getElementById('explanation-text');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
// --- QUIZ STATE ---
let currentQuestionIndex = 0;
const userAnswers = new Array(quizData.length).fill(null); // Stores { selectedIndex, isCorrect }
// --- FUNCTIONS ---
/**
* Loads and displays a question based on the current index.
*/
function loadQuestion() {
// Clear previous question's state
clearFeedback();
optionsContainerEl.innerHTML = '';
optionsContainerEl.classList.remove('answered');
// Get current question data
const currentQuestion = quizData[currentQuestionIndex];
// Display question text and counter
questionTextEl.textContent = currentQuestion.question;
questionCounterEl.textContent = `Question ${currentQuestionIndex + 1} of ${quizData.length}`;
// Create and display options
currentQuestion.options.forEach((option, index) => {
const optionId = `option-${currentQuestionIndex}-${index}`;
// Use a wrapper div for easier event handling
const optionWrapper = document.createElement('div');
optionWrapper.innerHTML = `
<input type="radio" name="option-${currentQuestionIndex}" id="${optionId}" class="option-input" value="${index}">
<label for="${optionId}" class="option-label" data-index="${index}">${option.value}</label>
`;
optionsContainerEl.appendChild(optionWrapper);
// Add event listener to the LABEL, not the hidden input
const label = optionWrapper.querySelector('label');
label.addEventListener('click', handleOptionSelect);
});
// Restore previous answer if it exists
if (userAnswers[currentQuestionIndex] !== null) {
restoreAnswerState();
}
updateNavigationButtons();
}
/**
* Handles the user clicking on an option.
* @param {Event} e - The click event object.
*/
function handleOptionSelect(e) {
// Prevent re-answering
if (optionsContainerEl.classList.contains('answered')) return;
optionsContainerEl.classList.add('answered');
const selectedLabel = e.target;
const selectedIndex = parseInt(selectedLabel.dataset.index);
const selectedInput = document.getElementById(`option-${currentQuestionIndex}-${selectedIndex}`);
selectedInput.checked = true; // Manually check the radio button
const currentQuestion = quizData[currentQuestionIndex];
const correctOption = currentQuestion.options.find(opt => opt.right);
const correctIndex = currentQuestion.options.indexOf(correctOption);
const isCorrect = selectedIndex === correctIndex;
// Store user's answer
userAnswers[currentQuestionIndex] = { selectedIndex, isCorrect };
// Provide visual feedback
showFeedback(selectedIndex, correctIndex, isCorrect);
}
/**
* Displays visual feedback (correct/incorrect) and the explanation.
*/
function showFeedback(selectedIndex, correctIndex, isCorrect) {
const labels = optionsContainerEl.querySelectorAll('.option-label');
// Highlight the correct answer in green
labels[correctIndex].classList.add('correct');
// If the user was wrong, highlight their choice in red
if (!isCorrect) {
labels[selectedIndex].classList.add('incorrect');
feedbackTextEl.textContent = "Incorrect!";
feedbackTextEl.className = 'incorrect';
} else {
feedbackTextEl.textContent = "Correct!";
feedbackTextEl.className = 'correct';
}
// Show explanation
explanationTextEl.textContent = quizData[currentQuestionIndex].explanation || '';
}
/**
* Restores the visual state if a question has been previously answered.
*/
function restoreAnswerState() {
optionsContainerEl.classList.add('answered');
const answer = userAnswers[currentQuestionIndex];
if (!answer) return;
// Check the previously selected radio button
const selectedInput = document.getElementById(`option-${currentQuestionIndex}-${answer.selectedIndex}`);
if(selectedInput) selectedInput.checked = true;
// Re-apply feedback styling
const currentQuestion = quizData[currentQuestionIndex];
const correctIndex = currentQuestion.options.findIndex(opt => opt.right);
showFeedback(answer.selectedIndex, correctIndex, answer.isCorrect);
}
/**
* Clears all feedback text and styling.
*/
function clearFeedback() {
feedbackTextEl.textContent = '';
explanationTextEl.textContent = '';
feedbackTextEl.className = '';
}
/**
* Updates the state and text of the navigation buttons.
*/
function updateNavigationButtons() {
prevBtn.disabled = currentQuestionIndex === 0;
nextBtn.disabled = currentQuestionIndex === quizData.length - 1 && userAnswers[currentQuestionIndex] === null;
if (currentQuestionIndex === quizData.length - 1) {
nextBtn.textContent = 'Finish Quiz';
} else {
nextBtn.textContent = 'Next';
}
}
/**
* Displays the final score to the user.
*/
function showFinalScore() {
const correctAnswers = userAnswers.filter(answer => answer && answer.isCorrect).length;
const totalQuestions = quizData.length;
quizContainer.innerHTML = `
<div id="final-score-container">
<h2>Quiz Completed!</h2>
<p>Your Score: ${correctAnswers} out of ${totalQuestions}</p>
<button class="nav-btn" onclick="location.reload()">Try Again</button>
</div>
`;
}
// --- EVENT LISTENERS ---
nextBtn.addEventListener('click', () => {
if (currentQuestionIndex < quizData.length - 1) {
currentQuestionIndex++;
loadQuestion();
} else {
// This is the "Finish Quiz" button
showFinalScore();
}
});
prevBtn.addEventListener('click', () => {
if (currentQuestionIndex > 0) {
currentQuestionIndex--;
loadQuestion();
}
});
// --- INITIALIZATION ---
// Load the first question when the script runs
document.addEventListener('DOMContentLoaded', loadQuestion);
</script>
</body>
</html>
