如何实现带三种答题模式的随机抽题测验应用
随机抽题测验应用实现方案
核心实现逻辑
- 题库按单选(radio)、下拉选择(dropdown)、文本输入(text)三类分别存储,方便按规则抽题
- 抽题时先从三类题型中各抽取1道保底,确保满足题型覆盖要求,剩余2道题从全题库随机抽取且不重复,最终打乱题目顺序
- 动态根据题型渲染对应交互组件,支持上下题切换、答案实时暂存,最后一题自动切换为提交按钮计算得分
- 完全适配你已完成的页面结构与样式,不需要修改现有CSS和HTML主体代码,仅需引入JS文件即可运行
使用说明
将下方JS代码保存为script.js,和你现有的HTML、CSS文件放在同一目录,在HTML文件的</body>闭合标签前添加以下代码引入JS:
<script src="script.js"></script>
完整JS代码
// 预设题库,可自行扩充题目内容 const questionBank = { radio: [ { id: 1, question: "以下哪个是JavaScript的原始数据类型?", options: ["Object", "Array", "String", "Function"], answer: "String" }, { id: 2, question: "CSS中哪个属性用于设置元素的外边距?", options: ["padding", "margin", "border", "outline"], answer: "margin" } ], dropdown: [ { id: 3, question: "HTML中用于定义无序列表的标签是?", options: ["<ol>", "<ul>", "<li>", "<dl>"], answer: "<ul>" }, { id: 4, question: "以下哪个方法可以向数组末尾添加元素?", options: ["shift()", "unshift()", "pop()", "push()"], answer: "push()" } ], text: [ { id: 5, question: "请写出CSS中设置flex布局主轴方向为横向的属性值:flex-direction: ____;", answer: "row" }, { id: 6, question: "JS中用于声明不可重新赋值的常量的关键字是?", answer: "const" } ] } // 全局状态管理 let selectedQuestions = [] let currentIndex = 0 let userAnswers = {} // 按规则抽取5道不重复、覆盖三类题型的题目 function getRandomQuestions() { const result = [] // 三类题型各抽1道保底 result.push(questionBank.radio[Math.floor(Math.random() * questionBank.radio.length)]) result.push(questionBank.dropdown[Math.floor(Math.random() * questionBank.dropdown.length)]) result.push(questionBank.text[Math.floor(Math.random() * questionBank.text.length)]) // 合并全量题库,抽取剩余2道题,做去重处理 const allQuestions = [...questionBank.radio, ...questionBank.dropdown, ...questionBank.text] while (result.length < 5) { const randomQ = allQuestions[Math.floor(Math.random() * allQuestions.length)] if (!result.find(q => q.id === randomQ.id)) { result.push(randomQ) } } // 打乱题目顺序 return result.sort(() => Math.random() - 0.5) } // 渲染当前索引对应的题目 function renderCurrentQuestion() { const quizContainer = document.querySelector('.quiz') const currentQ = selectedQuestions[currentIndex] // 识别当前题目题型 let questionType = 'radio' if (questionBank.dropdown.find(q => q.id === currentQ.id)) questionType = 'dropdown' if (questionBank.text.find(q => q.id === currentQ.id)) questionType = 'text' // 基础题目结构 let questionHtml = `<div class="question-item" style="color: #fff; margin: 40px 0;"> <p style="font-size: 22px; margin-bottom: 25px;">${currentIndex + 1}. ${currentQ.question}</p> ` // 按题型渲染对应交互组件 if (questionType === 'radio') { currentQ.options.forEach(option => { const checked = userAnswers[currentQ.id] === option ? 'checked' : '' questionHtml += `<label style="display: block; margin: 12px 0; font-size: 18px; cursor: pointer;"> <input type="radio" name="q${currentQ.id}" value="${option}" ${checked} style="margin-right: 10px; transform: scale(1.2);"> ${option} </label>` }) } else if (questionType === 'dropdown') { const savedValue = userAnswers[currentQ.id] || '' questionHtml += `<select name="q${currentQ.id}" style="padding: 10px 20px; width: 300px; font-size: 16px; border-radius: 5px; border: none;"> <option value="">请选择答案</option> ${currentQ.options.map(opt => `<option value="${opt}" ${savedValue === opt ? 'selected' : ''}>${opt}</option>`).join('')} </select>` } else if (questionType === 'text') { const savedValue = userAnswers[currentQ.id] || '' questionHtml += `<input type="text" name="q${currentQ.id}" value="${savedValue}" placeholder="请输入你的答案" style="padding: 10px 20px; width: 300px; font-size: 16px; border-radius: 5px; border: none;">` } questionHtml += `</div>` quizContainer.innerHTML = questionHtml // 绑定答案实时保存事件 bindAnswerSaveEvent(currentQ.id, questionType) // 更新切换按钮状态 updateToggleButtonStatus() } // 绑定答案输入/选择的实时保存逻辑 function bindAnswerSaveEvent(questionId, questionType) { if (questionType === 'radio') { const radioList = document.querySelectorAll(`input[name="q${questionId}"]`) radioList.forEach(radio => { radio.addEventListener('change', e => { userAnswers[questionId] = e.target.value }) }) } else if (questionType === 'dropdown') { const select = document.querySelector(`select[name="q${questionId}"]`) select.addEventListener('change', e => { userAnswers[questionId] = e.target.value }) } else if (questionType === 'text') { const input = document.querySelector(`input[name="q${questionId}"]`) input.addEventListener('input', e => { userAnswers[questionId] = e.target.value.trim() }) } } // 更新上下题/提交按钮的可用状态 function updateToggleButtonStatus() { const prevBtn = document.querySelector('.prev') const nextBtn = document.querySelector('.next') // 第一题禁用上一题按钮 prevBtn.disabled = currentIndex === 0 prevBtn.style.opacity = currentIndex === 0 ? '0.5' : '1' // 最后一题将下一题按钮改为提交 if (currentIndex === selectedQuestions.length - 1) { nextBtn.querySelector('span').textContent = '提交' nextBtn.querySelector('i').className = 'bx bx-check' } else { nextBtn.querySelector('span').textContent = 'Next' nextBtn.querySelector('i').className = 'bx bx-right-arrow-alt' } } // 计算答题得分 function calculateFinalScore() { let correctCount = 0 selectedQuestions.forEach(question => { // 文本答案不区分大小写匹配 if (userAnswers[question.id]?.toLowerCase() === question.answer.toLowerCase()) { correctCount++ } }) alert(`答题完成!你的得分是:${correctCount}/5`) // 重置测验 initQuiz() } // 初始化测验 function initQuiz() { selectedQuestions = getRandomQuestions() currentIndex = 0 userAnswers = {} renderCurrentQuestion() } // 绑定切换按钮事件 document.querySelector('.prev').addEventListener('click', () => { if (currentIndex > 0) { currentIndex-- renderCurrentQuestion() } }) document.querySelector('.next').addEventListener('click', () => { if (currentIndex === selectedQuestions.length - 1) { calculateFinalScore() return } currentIndex++ renderCurrentQuestion() }) // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', initQuiz)
你可以直接修改
questionBank里的题目内容、新增题目,代码会自动适配抽题逻辑,不需要修改其他核心逻辑。
内容的提问来源于stack exchange,提问作者Abdulhameed Mustapha
相关产品推荐
相关产品推荐

