如何为随机抽题测验应用实现单选按钮、文本输入答题模式
Web测验应用剩余功能实现方案
改造题库结构
首先给现有题库的每道题增加type字段标记题型,支持三类取值:
radio:单选按钮题select:下拉选择题(你已经实现的类型)input:文本输入题
文本输入题不需要设置a/b/c/d选项,直接存储标准答案即可,改造后的题库示例:
const quizQuestions = [ { type: "radio", question: "Which language runs in a web browser ?", a: "Python", b: "Javascript", c: "Java", d: "Flutter", ans: "b", }, { type: "select", question: "How many standard color names does HTML supports ?", a: "20", b: "90", c: "140", d: "125", ans: "c", }, { type: "radio", question: "Javascript displays numbers as base ?", a: "10", b: "2", c: "5", d: "12", ans: "a", }, { type: "radio", question: "Who is the father of computer ?", a: "Charles Babbage", b: "Allan Turing", c: "Issac Newton", d: "Bill Gate", ans: "a", }, { type: "radio", question: "The brain of a computer is ?", a: "ALU", b: "DVD", c: "CPU", d: "Memory", ans: "c", }, { type: "select", question: "What year was JavaScript launched ?", a: "1996", b: "1995", c: "1994", d: "1993", ans: "b", }, { type: "select", question: "Junk e-mail is called ?", a: "junk", b: "spoof", c: "sperm", d: "spam", ans: "d", }, { type: "radio", question: "Which tag is used for list-items ?", a: "<ol>", b: "<ul>", c: "<dl>", d: "<li>", ans: "d", }, { type: "select", question: "How old is Bill Gate ?", a: "59", b: "60", c: "56", d: "66", ans: "d", }, // 新增文本输入类题目,补足题型 { type: "input", question: "What is the abbreviation of HyperText Markup Language?", ans: "HTML" }, { type: "input", question: "Calculate the result of 8 * 7?", ans: "56" } ];
实现符合规则的随机抽题逻辑
抽题规则要求5道题至少覆盖1道单选、1道下拉、1道文本输入,实现逻辑为:先从三类题型中各抽1道保底,再从剩余题库中随机抽2道,最后打乱题目顺序避免题型固定排布,代码如下:
function pickRandomQuestions() { // 按题型分组 const radioPool = quizQuestions.filter(q => q.type === "radio"); const selectPool = quizQuestions.filter(q => q.type === "select"); const inputPool = quizQuestions.filter(q => q.type === "input"); // 三类题各抽1道保底 const picked = [ radioPool[Math.floor(Math.random() * radioPool.length)], selectPool[Math.floor(Math.random() * selectPool.length)], inputPool[Math.floor(Math.random() * inputPool.length)] ]; // 抽剩余2道题,排除已选题目避免重复 const restPool = quizQuestions.filter(q => !picked.includes(q)); for (let i = 0; i < 2; i++) { const randomIndex = Math.floor(Math.random() * restPool.length); picked.push(restPool.splice(randomIndex, 1)[0]); } // 打乱题目顺序 return picked.sort(() => Math.random() - 0.5); } // 初始化本次答题的题目列表、当前题号、用户答案存储 const currentQuizList = pickRandomQuestions(); let currentIndex = 0; const userAnswers = new Array(5).fill(null);
动态渲染三类答题控件
删掉HTML中写死的下拉选择结构,把选项容器改成空容器,方便动态渲染不同题型:
<div class="question-options" id="optionsContainer"> <!-- 题型控件动态生成 --> </div>
编写题目渲染函数,根据当前题目的type字段生成对应答题控件,同时支持答案回填:
const questionTextEl = document.getElementById("question"); const questionNoEl = document.querySelector(".question-no h3"); const optionsContainer = document.getElementById("optionsContainer"); function renderQuestion() { const currentQ = currentQuizList[currentIndex]; // 更新题号、题干 questionNoEl.textContent = `Question ${currentIndex + 1} of 5`; questionTextEl.textContent = currentQ.question; // 清空上一题的控件 optionsContainer.innerHTML = ""; // 渲染单选按钮题 if (currentQ.type === "radio") { ["a", "b", "c", "d"].forEach(optKey => { const label = document.createElement("label"); label.style.color = "#fff"; label.style.display = "flex"; label.style.alignItems = "center"; label.style.gap = "10px"; label.style.cursor = "pointer"; const radio = document.createElement("input"); radio.type = "radio"; radio.name = "answer"; radio.value = optKey; // 回填已选答案 if (userAnswers[currentIndex] === optKey) radio.checked = true; // 监听选择事件存答案 radio.addEventListener("change", () => { userAnswers[currentIndex] = optKey; }); label.append(radio, document.createTextNode(currentQ[optKey])); optionsContainer.appendChild(label); }); } // 渲染下拉选择题(原有逻辑) if (currentQ.type === "select") { const select = document.createElement("select"); select.innerHTML = `<option selected disabled>Choose your answer</option>`; ["a", "b", "c", "d"].forEach(optKey => { const opt = document.createElement("option"); opt.value = optKey; opt.textContent = currentQ[optKey]; if (userAnswers[currentIndex] === optKey) opt.selected = true; select.appendChild(opt); }); select.addEventListener("change", e => { userAnswers[currentIndex] = e.target.value; }); optionsContainer.appendChild(select); } // 渲染文本输入题 if (currentQ.type === "input") { const input = document.createElement("input"); input.type = "text"; input.placeholder = "Type your answer here"; input.style.padding = "8px"; input.style.border = "none"; input.style.outline = "none"; input.style.fontSize = "15px"; input.style.borderRadius = "4px"; // 回填已输入内容 if (userAnswers[currentIndex]) input.value = userAnswers[currentIndex]; // 监听输入存答案,自动去首尾空格、转小写,降低判分误差 input.addEventListener("input", e => { userAnswers[currentIndex] = e.target.value.trim().toLowerCase(); }); optionsContainer.appendChild(input); } } // 初始化渲染第一题 renderQuestion();
补全题目切换与判分逻辑
给上下题按钮绑定事件,支持切换题目、最后一题提交判分:
const prevBtn = document.querySelector(".prev"); const nextBtn = document.querySelector(".next"); prevBtn.addEventListener("click", () => { if (currentIndex > 0) { currentIndex--; renderQuestion(); } }); nextBtn.addEventListener("click", () => { if (currentIndex < 4) { currentIndex++; renderQuestion(); } else { // 计算得分 let score = 0; currentQuizList.forEach((q, idx) => { const correctAns = q.type === "input" ? q.ans.trim().toLowerCase() : q.ans; if (userAnswers[idx] === correctAns) score++; }); alert(`Quiz finished! Your score is ${score}/5`); } });
补充适配样式
在原有CSS中新增控件样式,保持视觉统一:
.question-options input[type="radio"] { width: 18px; height: 18px; cursor: pointer; }
内容的提问来源于stack exchange,提问作者Abdulhameed Mustapha
相关产品推荐
相关产品推荐

