实战项目4:问答系统

项目介绍

在这个项目中,我们将创建一个功能完整的问答系统,该系统允许用户回答一系列问题,并在完成后显示得分和结果反馈。这个项目将帮助我们学习JavaScript的状态管理、DOM操作、事件处理和定时器等核心概念。

项目需求

  1. 显示当前问题和选项
  2. 支持多选题和单选题
  3. 显示当前问题的序号和总题数
  4. 支持前进/后退导航
  5. 支持跳过问题
  6. 显示倒计时功能
  7. 支持实时计分
  8. 完成后显示详细的结果报告
  9. 显示正确答案和用户的选择
  10. 支持重新开始测验
  11. 页面布局简洁美观,响应式设计

技术栈

  • HTML5
  • CSS3
  • JavaScript (ES6+)

项目结构

quiz-system-project/
├── index.html      # 主页面
├── styles.css      # 样式文件
└── script.js       # JavaScript文件

实现步骤

1. 创建HTML结构

首先,我们需要创建HTML结构,包括问答系统的各个组件:标题、进度条、问题显示、选项区域、导航按钮和结果页面。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>问答系统</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div class="quiz-container">
        <div class="quiz-header">
            <h1 id="quiz-title">JavaScript知识问答</h1>
            <div class="quiz-info">
                <div class="question-counter">
                    <span id="current-question">1</span> / <span id="total-questions">0</span>
                </div>
                <div class="timer">
                    <span id="time-remaining">60</span>秒
                </div>
            </div>
        </div>
        
        <!-- 进度条 -->
        <div class="progress-container">
            <div class="progress-bar" id="progress-bar"></div>
        </div>
        
        <!-- 问题区域 -->
        <div class="question-section" id="question-section">
            <div class="question" id="question">问题将在这里显示</div>
            
            <!-- 选项区域 -->
            <div class="options-container" id="options-container"></div>
        </div>
        
        <!-- 导航按钮 -->
        <div class="navigation-buttons">
            <button id="prev-btn" class="nav-btn" disabled>上一题</button>
            <button id="next-btn" class="nav-btn primary">下一题</button>
            <button id="submit-btn" class="nav-btn submit-btn" style="display: none;">提交</button>
        </div>
        
        <!-- 结果页面 -->
        <div class="results-section" id="results-section" style="display: none;">
            <h2>测验结果</h2>
            <div class="score-card">
                <div class="final-score" id="final-score">0</div>
                <div class="score-text">
                    <span id="correct-answers">0</span> / <span id="total-questions-result">0</span> 正确
                </div>
                <div class="grade" id="grade">优秀</div>
            </div>
            
            <!-- 详细结果 -->
            <div class="detailed-results" id="detailed-results"></div>
            
            <!-- 重新开始按钮 -->
            <button id="restart-btn" class="nav-btn primary">重新开始</button>
        </div>
    </div>
    
    <script src="script.js"></script>
</body>
</html>

2. 添加CSS样式

接下来,我们需要添加CSS样式,使问答系统看起来更加美观和易用。

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Arial', sans-serif;
    background-color: #f5f5f5;
    color: #333;
    line-height: 1.6;
}

.quiz-container {
    background-color: white;
    border-radius: 10px;
    box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
    padding: 20px;
    max-width: 800px;
    width: 100%;
    margin: 50px auto;
}

/* 头部样式 */
.quiz-header {
    text-align: center;
    margin-bottom: 20px;
}

#quiz-title {
    color: #2c3e50;
    font-size: 28px;
    margin-bottom: 15px;
}

.quiz-info {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background-color: #ecf0f1;
    padding: 10px 20px;
    border-radius: 5px;
    font-weight: bold;
}

.question-counter {
    color: #34495e;
}

.timer {
    color: #e74c3c;
    font-size: 18px;
}

/* 进度条样式 */
.progress-container {
    background-color: #ecf0f1;
    border-radius: 10px;
    height: 10px;
    margin-bottom: 20px;
    overflow: hidden;
}

.progress-bar {
    background-color: #3498db;
    height: 100%;
    width: 0%;
    transition: width 0.3s ease;
    border-radius: 10px;
}

/* 问题区域样式 */
.question-section {
    margin-bottom: 20px;
}

.question {
    font-size: 20px;
    font-weight: bold;
    margin-bottom: 20px;
    color: #2c3e50;
    line-height: 1.4;
}

/* 选项区域样式 */
.options-container {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.option {
    background-color: #f8f9fa;
    border: 2px solid #e9ecef;
    border-radius: 8px;
    padding: 15px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    user-select: none;
}

.option:hover {
    background-color: #e9ecef;
    transform: translateX(5px);
}

.option.selected {
    background-color: #d1ecf1;
    border-color: #3498db;
}

.option.correct {
    background-color: #d4edda;
    border-color: #28a745;
}

.option.incorrect {
    background-color: #f8d7da;
    border-color: #dc3545;
}

.option input[type="radio"],
.option input[type="checkbox"] {
    margin-right: 15px;
    transform: scale(1.2);
}

.option-label {
    font-size: 16px;
    cursor: pointer;
}

/* 导航按钮样式 */
.navigation-buttons {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-top: 30px;
}

.nav-btn {
    background-color: #6c757d;
    color: white;
    border: none;
    padding: 12px 20px;
    font-size: 16px;
    border-radius: 5px;
    cursor: pointer;
    transition: all 0.3s ease;
    font-weight: bold;
    min-width: 120px;
}

.nav-btn:hover:not(:disabled) {
    background-color: #5a6268;
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}

.nav-btn:active:not(:disabled) {
    transform: translateY(0);
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.nav-btn:disabled {
    background-color: #e9ecef;
    color: #6c757d;
    cursor: not-allowed;
    opacity: 0.7;
}

.nav-btn.primary {
    background-color: #3498db;
}

.nav-btn.primary:hover:not(:disabled) {
    background-color: #2980b9;
}

.submit-btn {
    background-color: #28a745;
}

.submit-btn:hover:not(:disabled) {
    background-color: #218838;
}

/* 结果页面样式 */
.results-section {
    text-align: center;
    padding: 30px 0;
}

.results-section h2 {
    color: #2c3e50;
    margin-bottom: 30px;
    font-size: 28px;
}

.score-card {
    background-color: #f8f9fa;
    border-radius: 10px;
    padding: 30px;
    margin-bottom: 30px;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}

.final-score {
    font-size: 64px;
    font-weight: bold;
    color: #3498db;
    margin-bottom: 10px;
}

.score-text {
    font-size: 20px;
    color: #6c757d;
    margin-bottom: 15px;
}

.grade {
    font-size: 24px;
    font-weight: bold;
    padding: 10px 20px;
    border-radius: 20px;
    display: inline-block;
}

.grade.excellent {
    background-color: #d4edda;
    color: #155724;
}

.grade.good {
    background-color: #d1ecf1;
    color: #0c5460;
}

.grade.average {
    background-color: #fff3cd;
    color: #856404;
}

.grade.poor {
    background-color: #f8d7da;
    color: #721c24;
}

/* 详细结果样式 */
.detailed-results {
    margin-top: 30px;
    text-align: left;
}

.result-item {
    background-color: #f8f9fa;
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 15px;
    border-left: 5px solid #3498db;
}

.result-item.correct {
    border-left-color: #28a745;
}

.result-item.incorrect {
    border-left-color: #dc3545;
}

.result-question {
    font-weight: bold;
    margin-bottom: 10px;
    color: #2c3e50;
}

.result-options {
    margin-left: 20px;
}

.result-option {
    margin-bottom: 5px;
    padding-left: 5px;
}

.result-option.correct {
    color: #28a745;
    font-weight: bold;
}

.result-option.incorrect {
    color: #dc3545;
    text-decoration: line-through;
}

.result-option.selected {
    font-style: italic;
}

.result-explanation {
    margin-top: 10px;
    font-size: 14px;
    color: #6c757d;
    font-style: italic;
}

/* 响应式设计 */
@media (max-width: 768px) {
    .quiz-container {
        margin: 20px;
        padding: 15px;
    }
    
    #quiz-title {
        font-size: 24px;
    }
    
    .quiz-info {
        flex-direction: column;
        gap: 10px;
    }
    
    .question {
        font-size: 18px;
    }
    
    .navigation-buttons {
        flex-direction: column;
        gap: 15px;
    }
    
    .nav-btn {
        width: 100%;
    }
    
    .final-score {
        font-size: 48px;
    }
}

@media (max-width: 480px) {
    .quiz-container {
        margin: 10px;
        padding: 10px;
    }
    
    #quiz-title {
        font-size: 20px;
    }
    
    .question {
        font-size: 16px;
    }
    
    .option {
        padding: 12px;
    }
    
    .final-score {
        font-size: 36px;
    }
}

2. 编写JavaScript逻辑

最后,我们需要编写JavaScript逻辑,实现问答系统的所有功能。

// 问题数据
const quizData = [
    {
        question: "以下哪个不是JavaScript的基本数据类型?",
        type: "single",
        options: [
            { text: "String", correct: false },
            { text: "Number", correct: false },
            { text: "Boolean", correct: false },
            { text: "Object", correct: true }
        ],
        explanation: "JavaScript的基本数据类型包括String、Number、Boolean、Null、Undefined、Symbol和BigInt,而Object是引用数据类型。"
    },
    {
        question: "以下哪些是JavaScript的循环语句?",
        type: "multiple",
        options: [
            { text: "for", correct: true },
            { text: "while", correct: true },
            { text: "do-while", correct: true },
            { text: "loop", correct: false }
        ],
        explanation: "JavaScript的循环语句包括for、while、do-while和for...of、for...in等,但没有loop语句。"
    },
    {
        question: "以下哪个方法可以用来添加事件监听器?",
        type: "single",
        options: [
            { text: "addEventListener", correct: true },
            { text: "attachEvent", correct: false },
            { text: "onclick", correct: false },
            { text: "addEvent", correct: false }
        ],
        explanation: "addEventListener是标准的事件监听器添加方法,attachEvent是旧版IE浏览器的方法,onclick是事件属性,addEvent不是标准方法。"
    },
    {
        question: "以下哪些是JavaScript的数组方法?",
        type: "multiple",
        options: [
            { text: "push", correct: true },
            { text: "pop", correct: true },
            { text: "shift", correct: true },
            { text: "slice", correct: true },
            { text: "splice", correct: true },
            { text: "concat", correct: true }
        ],
        explanation: "以上所有方法都是JavaScript的数组方法,用于数组的增删改查操作。"
    },
    {
        question: "以下哪个关键字用于创建类?",
        type: "single",
        options: [
            { text: "class", correct: true },
            { text: "function", correct: false },
            { text: "object", correct: false },
            { text: "constructor", correct: false }
        ],
        explanation: "class关键字用于创建类,function用于创建函数,object不是关键字,constructor是类中的构造函数。"
    },
    {
        question: "以下哪些是JavaScript的异步编程方法?",
        type: "multiple",
        options: [
            { text: "setTimeout", correct: true },
            { text: "setInterval", correct: true },
            { text: "Promise", correct: true },
            { text: "async/await", correct: true },
            { text: "fetch", correct: true }
        ],
        explanation: "以上所有方法都是JavaScript的异步编程方法,用于处理异步操作。"
    },
    {
        question: "以下哪个方法可以用来解析JSON字符串?",
        type: "single",
        options: [
            { text: "JSON.parse()", correct: true },
            { text: "JSON.stringify()", correct: false },
            { text: "JSON.decode()", correct: false },
            { text: "JSON.encode()", correct: false }
        ],
        explanation: "JSON.parse()用于将JSON字符串解析为JavaScript对象,JSON.stringify()用于将JavaScript对象转换为JSON字符串。"
    },
    {
        question: "以下哪些是JavaScript的DOM选择器方法?",
        type: "multiple",
        options: [
            { text: "getElementById", correct: true },
            { text: "getElementsByClassName", correct: true },
            { text: "getElementsByTagName", correct: true },
            { text: "querySelector", correct: true },
            { text: "querySelectorAll", correct: true }
        ],
        explanation: "以上所有方法都是JavaScript的DOM选择器方法,用于选择DOM元素。"
    }
];

// DOM元素
const currentQuestionElement = document.getElementById('current-question');
const totalQuestionsElement = document.getElementById('total-questions');
const timeRemainingElement = document.getElementById('time-remaining');
const progressBarElement = document.getElementById('progress-bar');
const questionElement = document.getElementById('question');
const optionsContainerElement = document.getElementById('options-container');
const prevBtnElement = document.getElementById('prev-btn');
const nextBtnElement = document.getElementById('next-btn');
const submitBtnElement = document.getElementById('submit-btn');
const questionSectionElement = document.getElementById('question-section');
const resultsSectionElement = document.getElementById('results-section');
const finalScoreElement = document.getElementById('final-score');
const correctAnswersElement = document.getElementById('correct-answers');
const totalQuestionsResultElement = document.getElementById('total-questions-result');
const gradeElement = document.getElementById('grade');
const detailedResultsElement = document.getElementById('detailed-results');
const restartBtnElement = document.getElementById('restart-btn');

// 测验状态
let currentQuestionIndex = 0;
let score = 0;
let userAnswers = [];
let timeRemaining = 60;
let timer = null;
let quizCompleted = false;

// 初始化测验
function initQuiz() {
    // 初始化用户答案数组
    userAnswers = new Array(quizData.length).fill(null);
    
    // 设置总题数
    totalQuestionsElement.textContent = quizData.length;
    totalQuestionsResultElement.textContent = quizData.length;
    
    // 显示第一个问题
    showQuestion(currentQuestionIndex);
    
    // 启动计时器
    startTimer();
}

// 显示问题
function showQuestion(index) {
    // 更新当前问题索引
    currentQuestionIndex = index;
    
    // 更新问题计数器
    currentQuestionElement.textContent = index + 1;
    
    // 更新进度条
    const progressPercentage = ((index + 1) / quizData.length) * 100;
    progressBarElement.style.width = `${progressPercentage}%`;
    
    // 获取当前问题数据
    const currentQuestion = quizData[index];
    
    // 显示问题
    questionElement.textContent = currentQuestion.question;
    
    // 清空选项容器
    optionsContainerElement.innerHTML = '';
    
    // 生成选项
    currentQuestion.options.forEach((option, optionIndex) => {
        const optionElement = document.createElement('div');
        optionElement.className = 'option';
        
        // 根据问题类型创建不同的输入元素
        let inputElement;
        if (currentQuestion.type === 'single') {
            inputElement = document.createElement('input');
            inputElement.type = 'radio';
            inputElement.name = 'question' + index;
            inputElement.value = optionIndex;
        } else {
            inputElement = document.createElement('input');
            inputElement.type = 'checkbox';
            inputElement.name = 'question' + index;
            inputElement.value = optionIndex;
        }
        
        // 如果用户已经回答过这个问题,恢复选中状态
        if (userAnswers[index] !== null) {
            if (currentQuestion.type === 'single') {
                inputElement.checked = userAnswers[index] === optionIndex;
            } else {
                inputElement.checked = userAnswers[index].includes(optionIndex);
            }
        }
        
        // 创建选项标签
        const labelElement = document.createElement('label');
        labelElement.className = 'option-label';
        labelElement.textContent = option.text;
        
        // 将输入元素和标签添加到选项容器
        optionElement.appendChild(inputElement);
        optionElement.appendChild(labelElement);
        
        // 添加选项到选项容器
        optionsContainerElement.appendChild(optionElement);
        
        // 添加选项点击事件
        optionElement.addEventListener('click', () => {
            handleOptionSelect(optionElement, inputElement, currentQuestion.type);
        });
        
        // 添加输入元素的change事件
        inputElement.addEventListener('change', () => {
            handleOptionSelect(optionElement, inputElement, currentQuestion.type);
        });
    });
    
    // 更新导航按钮状态
    updateNavigationButtons();
}

// 处理选项选择
function handleOptionSelect(optionElement, inputElement, questionType) {
    // 获取当前问题的所有选项
    const allOptions = optionsContainerElement.querySelectorAll('.option');
    const allInputs = optionsContainerElement.querySelectorAll('input');
    
    // 移除所有选项的选中状态
    allOptions.forEach(opt => opt.classList.remove('selected'));
    
    // 处理单选题
    if (questionType === 'single') {
        // 只选中当前选项
        optionElement.classList.add('selected');
        
        // 保存用户答案
        userAnswers[currentQuestionIndex] = parseInt(inputElement.value);
    }
    // 处理多选题
    else {
        // 切换当前选项的选中状态
        optionElement.classList.toggle('selected');
        
        // 获取所有选中的选项
        const selectedInputs = optionsContainerElement.querySelectorAll('input:checked');
        const selectedValues = Array.from(selectedInputs).map(input => parseInt(input.value));
        
        // 保存用户答案
        userAnswers[currentQuestionIndex] = selectedValues;
    }
}

// 更新导航按钮状态
function updateNavigationButtons() {
    // 更新上一题按钮状态
    prevBtnElement.disabled = currentQuestionIndex === 0;
    
    // 更新下一题/提交按钮状态
    if (currentQuestionIndex === quizData.length - 1) {
        nextBtnElement.style.display = 'none';
        submitBtnElement.style.display = 'inline-block';
    } else {
        nextBtnElement.style.display = 'inline-block';
        submitBtnElement.style.display = 'none';
    }
}

// 开始计时器
function startTimer() {
    // 清除之前的计时器
    if (timer) {
        clearInterval(timer);
    }
    
    // 重置时间
    timeRemaining = 60;
    timeRemainingElement.textContent = timeRemaining;
    
    // 启动计时器
    timer = setInterval(() => {
        timeRemaining--;
        timeRemainingElement.textContent = timeRemaining;
        
        // 如果时间用完,自动提交
        if (timeRemaining <= 0) {
            clearInterval(timer);
            submitQuiz();
        }
        
        // 更新计时器样式
        if (timeRemaining <= 10) {
            timeRemainingElement.style.color = '#dc3545';
            timeRemainingElement.style.fontWeight = 'bold';
        } else {
            timeRemainingElement.style.color = '';
            timeRemainingElement.style.fontWeight = '';
        }
    }, 1000);
}

// 计算得分
function calculateScore() {
    score = 0;
    
    quizData.forEach((question, index) => {
        const userAnswer = userAnswers[index];
        
        // 如果用户没有回答,跳过
        if (userAnswer === null) {
            return;
        }
        
        // 处理单选题
        if (question.type === 'single') {
            if (question.options[userAnswer].correct) {
                score++;
            }
        }
        // 处理多选题
        else {
            // 检查用户的选择是否与正确答案完全匹配
            const correctOptions = question.options
                .map((option, i) => option.correct ? i : null)
                .filter(i => i !== null);
            
            // 检查长度是否相同
            if (userAnswer.length === correctOptions.length) {
                // 检查所有选项是否都正确
                const allCorrect = userAnswer.every(answer => correctOptions.includes(answer));
                if (allCorrect) {
                    score++;
                }
            }
        }
    });
    
    return score;
}

// 获取等级
function getGrade(score, total) {
    const percentage = (score / total) * 100;
    
    if (percentage >= 90) {
        return { text: '优秀', class: 'excellent' };
    } else if (percentage >= 80) {
        return { text: '良好', class: 'good' };
    } else if (percentage >= 60) {
        return { text: '及格', class: 'average' };
    } else {
        return { text: '不及格', class: 'poor' };
    }
}

// 显示结果
function showResults() {
    // 隐藏问题区域,显示结果区域
    questionSectionElement.style.display = 'none';
    resultsSectionElement.style.display = 'block';
    
    // 停止计时器
    if (timer) {
        clearInterval(timer);
    }
    
    // 计算得分
    const correctAnswers = calculateScore();
    const totalQuestions = quizData.length;
    const percentage = Math.round((correctAnswers / totalQuestions) * 100);
    
    // 获取等级
    const grade = getGrade(correctAnswers, totalQuestions);
    
    // 更新结果显示
    finalScoreElement.textContent = percentage + '%';
    correctAnswersElement.textContent = correctAnswers;
    totalQuestionsResultElement.textContent = totalQuestions;
    gradeElement.textContent = grade.text;
    gradeElement.className = `grade ${grade.class}`;
    
    // 显示详细结果
    showDetailedResults();
}

// 显示详细结果
function showDetailedResults() {
    // 清空详细结果容器
    detailedResultsElement.innerHTML = '';
    
    // 遍历所有问题
    quizData.forEach((question, index) => {
        const userAnswer = userAnswers[index];
        
        // 创建结果项
        const resultItem = document.createElement('div');
        resultItem.className = 'result-item';
        
        // 检查答案是否正确
        let isCorrect = false;
        if (userAnswer !== null) {
            if (question.type === 'single') {
                isCorrect = question.options[userAnswer].correct;
            } else {
                const correctOptions = question.options
                    .map((option, i) => option.correct ? i : null)
                    .filter(i => i !== null);
                
                isCorrect = userAnswer.length === correctOptions.length && 
                           userAnswer.every(answer => correctOptions.includes(answer));
            }
        }
        
        // 添加正确/错误类
        resultItem.classList.add(isCorrect ? 'correct' : 'incorrect');
        
        // 创建问题元素
        const questionElement = document.createElement('div');
        questionElement.className = 'result-question';
        questionElement.textContent = `${index + 1}. ${question.question}`;
        
        // 创建选项容器
        const optionsContainer = document.createElement('div');
        optionsContainer.className = 'result-options';
        
        // 添加选项到选项容器
        question.options.forEach((option, optionIndex) => {
            const optionElement = document.createElement('div');
            optionElement.className = 'result-option';
            
            // 检查选项是否正确
            if (option.correct) {
                optionElement.classList.add('correct');
            }
            
            // 检查选项是否被用户选中
            let isSelected = false;
            if (userAnswer !== null) {
                if (question.type === 'single') {
                    isSelected = userAnswer === optionIndex;
                } else {
                    isSelected = userAnswer.includes(optionIndex);
                }
            }
            
            if (isSelected) {
                optionElement.classList.add('selected');
                // 如果选项被选中但不正确,添加错误类
                if (!option.correct) {
                    optionElement.classList.add('incorrect');
                }
            }
            
            // 设置选项文本
            optionElement.textContent = option.text;
            
            // 添加选项到选项容器
            optionsContainer.appendChild(optionElement);
        });
        
        // 创建解释元素
        const explanationElement = document.createElement('div');
        explanationElement.className = 'result-explanation';
        explanationElement.textContent = question.explanation;
        
        // 将元素添加到结果项
        resultItem.appendChild(questionElement);
        resultItem.appendChild(optionsContainer);
        resultItem.appendChild(explanationElement);
        
        // 将结果项添加到详细结果容器
        detailedResultsElement.appendChild(resultItem);
    });
}

// 更新导航按钮
function updateNavigationButtons() {
    // 更新上一题按钮状态
    prevBtnElement.disabled = currentQuestionIndex === 0;
    
    // 更新下一题/提交按钮状态
    if (currentQuestionIndex === quizData.length - 1) {
        nextBtnElement.style.display = 'none';
        submitBtnElement.style.display = 'inline-block';
    } else {
        nextBtnElement.style.display = 'inline-block';
        submitBtnElement.style.display = 'none';
    }
}

// 导航到上一题
function goToPreviousQuestion() {
    if (currentQuestionIndex > 0) {
        currentQuestionIndex--;
        showQuestion(currentQuestionIndex);
    }
}

// 导航到下一题
function goToNextQuestion() {
    if (currentQuestionIndex < quizData.length - 1) {
        currentQuestionIndex++;
        showQuestion(currentQuestionIndex);
    }
}

// 提交测验
function submitQuiz() {
    quizCompleted = true;
    showResults();
}

// 重新开始测验
function restartQuiz() {
    // 重置状态
    currentQuestionIndex = 0;
    score = 0;
    userAnswers = [];
    timeRemaining = 60;
    quizCompleted = false;
    
    // 显示问题区域,隐藏结果区域
    questionSectionElement.style.display = 'block';
    resultsSectionElement.style.display = 'none';
    
    // 重新初始化测验
    initQuiz();
}

// 添加事件监听器
prevBtnElement.addEventListener('click', goToPreviousQuestion);
nextBtnElement.addEventListener('click', goToNextQuestion);
submitBtnElement.addEventListener('click', submitQuiz);
restartBtnElement.addEventListener('click', restartQuiz);

// 初始化测验
initQuiz();

功能说明

  1. 问题展示:显示当前问题和选项,支持单选题和多选题。
  2. 进度追踪:显示当前问题序号、总题数和进度条。
  3. 倒计时功能:每道题限时60秒,时间用完自动提交。
  4. 导航功能:支持前进/后退导航,方便用户修改答案。
  5. 实时反馈:选项被选中时会有视觉反馈。
  6. 自动计分:完成后自动计算得分和正确率。
  7. 详细结果:显示每道题的正确答案、用户选择和解释。
  8. 等级评定:根据得分评定等级(优秀、良好、及格、不及格)。
  9. 重新开始:支持重新开始测验。
  10. 响应式设计:适配不同屏幕尺寸,在手机和电脑上都能正常使用。

扩展功能

我们可以进一步扩展问答系统的功能,例如:

  1. 支持多种题型:填空题、判断题、简答题等。
  2. 支持随机出题:每次测验随机抽取一定数量的题目。
  3. 支持题目分类:按难度或知识点分类。
  4. 支持用户登录:保存用户的测验历史和成绩。
  5. 支持时间设置:允许管理员设置每道题的时间。
  6. 支持题目导入导出:支持从JSON或CSV文件导入题目。
  7. 支持图片和视频题目:允许在题目中插入图片和视频。
  8. 支持音效反馈:选择答案或完成测验时播放音效。

扩展功能示例:随机出题

// 扩展:随机出题功能
function getRandomQuestions(data, count) {
    // 创建问题数据的副本
    const questionsCopy = [...data];
    const randomQuestions = [];
    
    // 随机抽取指定数量的题目
    for (let i = 0; i < count && questionsCopy.length > 0; i++) {
        const randomIndex = Math.floor(Math.random() * questionsCopy.length);
        randomQuestions.push(questionsCopy[randomIndex]);
        questionsCopy.splice(randomIndex, 1);
    }
    
    return randomQuestions;
}

// 使用随机出题功能
const randomQuizData = getRandomQuestions(quizData, 5); // 随机抽取5道题

扩展功能示例:多种题型支持

// 扩展:支持判断题
const quizDataWithTrueFalse = [
    {
        question: "JavaScript是一种编译型语言。",
        type: "truefalse",
        correct: false,
        explanation: "JavaScript是一种解释型语言,不是编译型语言。"
    }
];

// 在showQuestion函数中添加判断题处理
if (currentQuestion.type === 'truefalse') {
    // 创建判断题选项
    const trueOption = createOption('正确', 'true');
    const falseOption = createOption('错误', 'false');
    optionsContainerElement.appendChild(trueOption);
    optionsContainerElement.appendChild(falseOption);
}

项目运行

  1. 创建上述三个文件(index.html, styles.css, script.js)
  2. 在浏览器中打开index.html文件
  3. 开始使用问答系统

项目总结

通过这个问答系统项目,我们学习了:

  1. 如何使用JavaScript进行状态管理
  2. 如何处理不同类型的表单输入
  3. 如何使用DOM操作动态更新页面内容
  4. 如何使用定时器实现倒计时功能
  5. 如何实现复杂的导航逻辑
  6. 如何计算得分和生成结果报告
  7. 如何实现响应式设计

这个项目涵盖了JavaScript开发中的许多重要概念,为我们后续开发更复杂的应用打下了基础。

练习

  1. 添加填空题支持
  2. 实现随机出题功能
  3. 添加题目分类功能
  4. 实现时间设置功能
  5. 添加题目导入导出功能
  6. 实现用户登录功能
  7. 添加音效反馈功能
  8. 实现排行榜功能

扩展阅读

通过这个实战项目,我们已经掌握了JavaScript的状态管理和复杂交互逻辑,接下来我们将继续学习更复杂的JavaScript项目。

« 上一篇 实战项目3:计算器 下一篇 » 实战项目5:天气应用