Practice MCQs with clickable question palette is an excellent feature for a long quiz. It allows users to see their progress at a glance and navigate easily.

Dynamic Quiz (100 Questions)

-----------------------------------------

CODE:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dynamic Quiz (100 Questions)</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: 20px;
        }

        /* 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: 700px; /* Increased width to accommodate palette */
            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;
        }

        .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;
        }
        
        /* Style for the selected state (blue inner circle) */
        .option-input:checked + .option-label::before {
            background-color: #2196f3;
            border-color: #2196f3;
            background-clip: content-box;
            padding: 3px;
        }
        
        /* Feedback Styling */
        .option-label.correct { background-color: #e8f5e9; border-color: #4caf50; }
        .option-label.incorrect { background-color: #ffebee; border-color: #f44336; }
        .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; }
        #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; }

        /* --- NEW STYLES for Question Palette --- */
        #question-palette-container {
            margin-top: 1.5rem;
            padding-top: 1.5rem;
            border-top: 1px solid #e0e0e0;
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            justify-content: center;
        }

        .palette-btn {
            width: 35px;
            height: 35px;
            border-radius: 50%; /* Makes it a circle */
            border: 1px solid #ccc;
            background-color: #f0f0f0; /* Default gray for not visited */
            color: #333;
            font-size: 0.9rem;
            font-weight: bold;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: background-color 0.2s, border-color 0.2s;
        }

        .palette-btn:hover {
            border-color: #333;
        }

        .palette-btn.correct {
            background-color: #4caf50; /* Green */
            border-color: #388e3c;
            color: white;
        }

        .palette-btn.incorrect {
            background-color: #f44336; /* Red */
            border-color: #d32f2f;
            color: white;
        }

        .palette-btn.current {
            border: 3px solid #2196f3; /* Blue border for current */
            box-shadow: 0 0 5px rgba(33, 150, 243, 0.5);
        }
        
    </style>
</head>
<body>

    <div id="quiz-container">
        <!-- Question content -->
        <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>
        <!-- Palette will be inserted here by JS -->
        <div id="question-palette-container"></div>
    </div>

    <script>
        // --- QUIZ DATA ---
        function generateDummyData(count) {
            const data = [];
            for (let i = 0; i < count; i++) {
                const correctIndex = i % 4;
                const options = [];
                for (let j = 0; j < 4; j++) {
                    options.push({ value: `Option ${j + 1}`, right: j === correctIndex });
                }
                data.push({
                    question: `Question ${i + 1}`,
                    explanation: `This is the explanation for Question ${i + 1}. The correct answer was Option ${correctIndex + 1}.`,
                    options: options
                });
            }
            return data;
        }
        const quizData = generateDummyData(100);

        // --- 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');
        const paletteContainerEl = document.getElementById('question-palette-container'); // New reference

        // --- QUIZ STATE ---
        let currentQuestionIndex = 0;
        const userAnswers = new Array(quizData.length).fill(null);

        // --- FUNCTIONS ---

        /**
         * Loads and displays a question and updates the palette.
         */
        function loadQuestion() {
            clearFeedback();
            optionsContainerEl.innerHTML = '';
            optionsContainerEl.classList.remove('answered');

            const currentQuestion = quizData[currentQuestionIndex];
            
            questionTextEl.textContent = currentQuestion.question;
            questionCounterEl.textContent = `Question ${currentQuestionIndex + 1} of ${quizData.length}`;

            currentQuestion.options.forEach((option, index) => {
                const optionId = `option-${currentQuestionIndex}-${index}`;
                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);
                const label = optionWrapper.querySelector('label');
                label.addEventListener('click', handleOptionSelect);
            });
            
            if (userAnswers[currentQuestionIndex] !== null) {
                restoreAnswerState();
            }

            updateNavigationButtons();
            updateQuestionPalette(); // Update palette on every question load
        }
        
        /**
         * Handles the user clicking on an option.
         */
        function handleOptionSelect(e) {
            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;

            const currentQuestion = quizData[currentQuestionIndex];
            const correctIndex = currentQuestion.options.findIndex(opt => opt.right);
            const isCorrect = selectedIndex === correctIndex;

            userAnswers[currentQuestionIndex] = { selectedIndex, isCorrect };

            showFeedback(selectedIndex, correctIndex, isCorrect);
            updateQuestionPalette(); // Update palette after an answer
        }

        /**
         * Displays visual feedback (correct/incorrect) and the explanation.
         */
        function showFeedback(selectedIndex, correctIndex, isCorrect) {
            const labels = optionsContainerEl.querySelectorAll('.option-label');
            labels[correctIndex].classList.add('correct');
            
            if (!isCorrect) {
                labels[selectedIndex].classList.add('incorrect');
                feedbackTextEl.textContent = "Incorrect!";
                feedbackTextEl.className = 'incorrect';
            } else {
                feedbackTextEl.textContent = "Correct!";
                feedbackTextEl.className = 'correct';
            }
            explanationTextEl.textContent = quizData[currentQuestionIndex].explanation || '';
        }
        
        function restoreAnswerState() {
            optionsContainerEl.classList.add('answered');
            const answer = userAnswers[currentQuestionIndex];
            if (!answer) return;

            const selectedInput = document.getElementById(`option-${currentQuestionIndex}-${answer.selectedIndex}`);
            if (selectedInput) selectedInput.checked = true;
            
            const correctIndex = quizData[currentQuestionIndex].options.findIndex(opt => opt.right);
            showFeedback(answer.selectedIndex, correctIndex, answer.isCorrect);
        }

        function clearFeedback() {
            feedbackTextEl.textContent = '';
            explanationTextEl.textContent = '';
            feedbackTextEl.className = '';
        }

        function updateNavigationButtons() {
            prevBtn.disabled = currentQuestionIndex === 0;
            nextBtn.disabled = userAnswers[currentQuestionIndex] === null && currentQuestionIndex === quizData.length - 1;

            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;
            
            // Re-use the existing structure to show the final score
            const mainContent = document.querySelector('#quiz-container > div:not(#question-palette-container)');
            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>
            `;
            // Append the final state of the palette
            quizContainer.appendChild(paletteContainerEl);
            // Disable buttons in the palette on the final screen
            paletteContainerEl.querySelectorAll('.palette-btn').forEach(btn => btn.disabled = true);
        }

        // --- NEW PALETTE FUNCTIONS ---

        /**
         * Creates the question number palette buttons once on startup.
         */
        function createQuestionPalette() {
            quizData.forEach((_, index) => {
                const button = document.createElement('button');
                button.className = 'palette-btn';
                button.textContent = index + 1;
                button.dataset.index = index;
                button.addEventListener('click', () => jumpToQuestion(index));
                paletteContainerEl.appendChild(button);
            });
        }

        /**
         * Updates the colors and state of the palette buttons.
         */
        function updateQuestionPalette() {
            const buttons = paletteContainerEl.querySelectorAll('.palette-btn');
            buttons.forEach((button, index) => {
                // Reset classes
                button.classList.remove('correct', 'incorrect', 'current');

                const answer = userAnswers[index];
                if (answer) {
                    button.classList.add(answer.isCorrect ? 'correct' : 'incorrect');
                }

                if (index === currentQuestionIndex) {
                    button.classList.add('current');
                }
            });
        }

        /**
         * Navigates directly to a specific question index.
         * @param {number} index - The index of the question to jump to.
         */
        function jumpToQuestion(index) {
            currentQuestionIndex = index;
            loadQuestion();
        }

        // --- EVENT LISTENERS ---
        nextBtn.addEventListener('click', () => {
            if (currentQuestionIndex < quizData.length - 1) {
                currentQuestionIndex++;
                loadQuestion();
            } else {
                showFinalScore();
            }
        });

        prevBtn.addEventListener('click', () => {
            if (currentQuestionIndex > 0) {
                currentQuestionIndex--;
                loadQuestion();
            }
        });

        // --- INITIALIZATION ---
        document.addEventListener('DOMContentLoaded', () => {
            createQuestionPalette();
            loadQuestion();
        });

    </script>
</body>
</html>
Tags