ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

纯前端HTML在线考试模板:倒计时、自动判分与进度保存

2026/9/15 2:32:10 拓冰建站 浏览量
纯前端HTML在线考试模板:倒计时、自动判分与进度保存 简介这是一份基于jQuery的HTML远程教育在线考试答题页面模板面向需要快速搭建在线考试界面或学习前端交互开发的开发者与教育机构。压缩包共8个文件包括1个主HTML页面、3个CSS样式文件、3个JavaScript脚本以及1个readme说明文档脚本中除jquery-1.11.3.min.js外还内置倒计时、答题进度饼图等常用组件整体仅58KB轻量易集成。目前已有998人学习下载。模板采用左侧题目区、右侧答题卡的经典结构左侧可支持选择题、填空题、问答题及图片多媒体展示右侧根据题型提供单选、多选或文本输入控件借助jQuery实现试题与答题卡双向同步、已答/未答标记、剩余时间倒计时、自动保存等交互同时融入响应式设计思路。开发者可将其作为在线考试系统的前端原型直接复用现成组件也可在readme引导下快速二次开发定制符合自身教学需求的答题页面。1. 远程教育答题页面模板先搞清楚要交付什么一位做远程教育的老师找到你说下周要给在线学生做一次随堂测验平台里没有原生考试模块希望你能给一个“html远程教育在线考试答题页面网站模板”。多数人第一反应是写一堆题目和input标签但真正让这个页面像考试系统的反而不是题目本身而是答题状态管理倒计时走完自动交卷、刷新页面后答案还在、交卷前提示还有哪些题没做。这个模板的价值在于它不需要后端打开浏览器就能用也能被嵌入到 Moodle、学习通或其他 LMS 的 iframe 容器里。适合前端入门者练手也适合需要快速交付演示页的工程师拿去改。2. 搭建答题页面的 HTML 骨架从 DOCTYPE 到表单控件在线考试页面的本质是一个复杂表单所以第一版结构必须围绕form展开。常见做法是用radio做单选题、checkbox做多选题再用textarea做简答题。不要一开始就上 Vue 或 React纯 HTML CSS JS 基础语法足以覆盖九成考试场景而且模板的通用性更强。2.1 一套标准化的 html 文档头与 viewport 设置无论后续加多少样式和脚本文档头都要先写对。很多 html 文件无法预览问题往往就出在缺少!DOCTYPE html或字符集写错。下面这段是模板的基础骨架所有远程教育页面都应该从它开始。!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1 title远程教育在线考试答题页面/title /head body form idexamForm !-- 题目区在这里插入 -- /form /body /htmllangzh-cn告诉搜索引擎和屏幕阅读器这个页面使用简体中文viewport的widthdevice-width让页面在手机浏览器上不再默认缩放这直接关系到学生用手机答题时的体验。form是整个答题页的容器后续的题目、按钮、隐藏域都要放在它里面提交事件也统一挂在它身上。2.2 题目区与选项区的标准化写法单选题的标准结构是每组题用一个fieldset或div包裹选项用label包住input这样点击文字也能选中答案。不要只用input裸放在页面上那样选中区域太小触屏设备上很容易点错。fieldset classquestion>table classanswer-card tr tda href#q1 classcard-item1/a/td tda href#q2 classcard-item2/a/td tda href#q3 classcard-item3/a/td /tr /table div classexam-status span剩余时间strong idtimer40:00/strong/span span已答strong idansweredCount0/strong/span idtotalCount3/span/span /diva href#q1会把页面滚动到对应题目学生不用拖着滚轮找题。exam-status里的timer、answeredCount、totalCount是三个需要被 JavaScript 控制的锚点后面所有倒计时和进度更新都围绕它们发生。这里用span而不是div是为了让状态栏保持一行也方便样式上做成胶囊形态。3. 用 CSS 和原生 JS 做出倒计时与答题状态联动有了 HTML 骨架之后需要让页面“活”起来。这个阶段不引入任何框架只用 CSS 控制视觉反馈用setInterval驱动倒计时用change事件更新已答数量。这既是 htmlcssjs 网页设计的标准练习也是模板能拿去演示的关键。3.1 答题项的高亮与禁用态考试页面里最常用的 CSS 技巧是“选中高亮”。通过:checked和相邻选择器不用写一行 JS 就能让被选中的label变色。另外交卷后要禁选用input:disabled label把选项置灰即可。下面是一组可直接抄用的样式。.question { border: 1px solid #d9d9d9; border-radius: 8px; padding: 16px; margin-bottom: 16px; background: #fff; } .question label { display: block; padding: 10px 12px; margin: 4px 0; border-radius: 4px; border: 1px solid #eee; cursor: pointer; transition: background 0.2s ease, border-color 0.2s ease; } .question input[typeradio]:checked label, .question input[typecheckbox]:checked label { background: #e6f7ff; border-color: #1890ff; } .question input:disabled label { opacity: 0.5; cursor: not-allowed; background: #fafafa; }transition在这里让背景色和边框色的变化有 0.2 秒的渐变视觉上比瞬间跳变舒服得多。:checked label是核心规则它依赖 HTML 里input必须紧跟在label之前或作为label的第一个子元素如果你的选项结构是inputspan文字/span/label选择器要改成input:checked span。禁用态把光标改成not-allowed明确告诉学生这道题不能再改了。3.2 倒计时、自动交卷与未答提醒的联动倒计时用原生 JS 的setInterval实现。注意不要把时间存在页面上否则刷新就要重新计时正确做法是记录“截止时间戳”每次tick用当前时间减去截止时间。const examForm document.getElementById(examForm); const timerEl document.getElementById(timer); const answeredCountEl document.getElementById(answeredCount); const totalCountEl document.getElementById(totalCount); let duration 40 * 60; // 秒 let deadline Date.now() duration * 1000; function updateTimer() { const remain Math.max(0, Math.floor((deadline - Date.now()) / 1000)); const minutes String(Math.floor(remain / 60)).padStart(2, 0); const seconds String(remain % 60).padStart(2, 0); timerEl.textContent ${minutes}:${seconds}; if (remain 0) { clearInterval(timerInterval); submitExam(); } } const timerInterval setInterval(updateTimer, 1000); updateTimer();为什么用Date.now() duration而不是duration--因为setInterval在标签页切到后台时会被浏览器节流每秒减一的写法会变慢考试时间就不准了。padStart保证秒数始终是两位数显示成04:05而不是4:5。deadline也可以只存一次到sessionStorage这样刷新页面时倒计时不会归零这个细节放在第 4 章一起讲。3.3 移动端适配与答题卡响应式用手机考试的学生不在少数模板必须做响应式。这里给两个断点屏幕小于640px时答题卡从侧边栏变成顶部横向滚动条小于480px时题目内边距缩小避免文字贴边。.exam-layout { display: grid; grid-template-columns: 1fr 240px; gap: 20px; } media (max-width: 640px) { .exam-layout { grid-template-columns: 1fr; } .answer-card { display: flex; overflow-x: auto; gap: 8px; } .answer-card td { display: inline-flex; } } media (max-width: 480px) { .question { padding: 12px; } .question label { padding: 8px 10px; } }grid-template-columns: 1fr 240px表示左侧题目区占满剩余宽度右侧答题卡固定 240px。窄屏下答题卡改成 Flex 横向滚动学生滑动查看题号。注意.answer-card td的display: inline-flex要把原来的表格单元格语义打破所以overflow-x要加在父容器上。下面这个表是常用属性速查方便改模板时对齐。属性取值用途grid-template-columns1fr 240px双栏布局右侧固定宽度overflow-xauto答题卡窄屏横向滚动transitionbackground .2s ease选中高亮过渡动画opacity.5交卷后选项禁用置灰4. JavaScript 判分与本地存储离线答题数据怎么留住考试页面最怕的是答到一半误刷新所有选项清空。远程教育场景下网络不稳定很常见所以模板必须把答题进度写进localStorage刷新后自动恢复。这一章同时解决两个问题前端判分怎么算数据怎么存。4.1 用 FormData 或 querySelectorAll 完成判分判分的核心是“遍历每道题读出选中项和标准答案比对”。推荐直接用FormData读取表单因为它天然支持同名 checkbox 的取值不需要手动维护选择状态。const answerKey { q1: A, q2: [A, B, D] }; function submitExam() { const formData new FormData(examForm); let score 0; // 单选题 if (formData.get(q1) answerKey.q1) score 20; // 多选题先排序再比较 const q2Answer formData.getAll(q2).sort().join(); const q2Key answerKey.q2.slice().sort().join(); if (q2Answer q2Key) score 20; alert(您的得分是 ${score} 分); examForm.classList.add(submitted); document.querySelectorAll(.question input).forEach(input input.disabled true); }formData.get(q1)拿到的是当前选中 radio 的value如果没有选中则返回null。多选题用getAll()拿到一个数组排序后用join()转成字符串再做比较这样避免选项顺序影响结论。交卷后给整个表单加一个submitted类名CSS 里对应的input:disabled label样式就会生效。判分逻辑写在前端只适合演示和模拟考试真实考试必须把答案放在服务端校验否则学生一开控制台就能看见answerKey。4.2 用 localStorage 保存答题进度与倒计时localStorage 的粒度有两种一种是把整个表单序列化成一个对象另一种是给每道题单独一个键。模板里推荐第二种因为恢复时不需要重新解析整张表单性能更好也方便单独修改某一题的状态。function saveProgress() { const formData new FormData(examForm); const answers {}; for (const [name, value] of formData.entries()) { if (!answers[name]) answers[name] []; answers[name].push(value); } localStorage.setItem(exam_progress, JSON.stringify(answers)); localStorage.setItem(exam_deadline, deadline); } function restoreProgress() { const saved localStorage.getItem(exam_progress); if (!saved) return; const answers JSON.parse(saved); for (const [name, values] of Object.entries(answers)) { const inputs examForm.querySelectorAll([name${name}]); inputs.forEach(input { if (values.includes(input.value)) { input.checked true; } }); } const savedDeadline localStorage.getItem(exam_deadline); if (savedDeadline) { deadline Number(savedDeadline); } }saveProgress每次change事件触发时调用把 FormData 转换成键值对对象存起来。存数组的原因是 checkbox 同名values.includes(input.value)能同时回填单选题和多选题。倒计时单独存在exam_deadline里恢复时直接用原来的截止时间刷新页面后继续倒数而不是重新开始这才是考试系统该有的行为。4.3 三个常见坑radio 取消选择、数据格式、存储上限第一个坑是 radio 一经选中就无法通过点击取消。想给学生“清除本题答案”的功能不能用纯 HTML 实现常见做法是在每题下面放一个“清除”按钮点击后遍历题目内所有 radio 并把它们的checked置为false。第二个坑是localStorage.getItem返回的是字符串布尔值、数字、对象必须手动转换。尤其是deadline存进去的是时间戳数字读出来是字符串直接用Number()包一层。第三个坑是 localStorage 容量大约 5MB纯文本答卷不会超但如果题目附带了 base64 图片就要改用 IndexedDB否则写入时会直接抛异常。下面这张表给出键名建议方便模板里统一管理。存储键内容类型exam_progress每题选中值的对象JSON 字符串exam_deadline截止时间戳数字字符串exam_submitted是否已交卷1/05. 模板落地的三个收尾技巧打印、答案数据化和样式调整模板做到能答题、能判分、能恢复进度基本可以交付了。最后再补三个高频需求学生要纸质存档、老师要批量改题、手机端体验微调。这三个技巧能让模板从“能用”变成“好用”。第一个技巧是打印样式。考试结束后学生经常要打印页面留底默认打印会把导航栏、按钮也打出来。在head里加一段media print样式只显示题目和选中项隐藏倒计时和交卷按钮。media print { .exam-status, .answer-card, .submit-btn, .clear-btn { display: none !important; } .question { border: none; padding: 8px 0; } .question label { border: none; } }第二个技巧是答案数据化。现在模板里的题目是写死在 HTML 的真要放进课程用每学期换题就是噩梦。把题目抽成一个数组用 JavaScript 渲染改题只需要改数组。const questions [ { id: q1, type: radio, question: HTML 指的是, options: [超文本标记语言, 高级文本语言, 超链接文本语言, 通用标记语言] } ]; function renderQuestion(q) { const fieldset document.createElement(fieldset); fieldset.className question; fieldset.id q.id; fieldset.dataset.index q.id.replace(q, ); fieldset.innerHTML legend${q.question}/legend q.options.map((opt, i) labelinput type${q.type} name${q.id} value${String.fromCharCode(65 i)} ${opt}/label ).join(); return fieldset; }String.fromCharCode(65 i)把索引0,1,2,3转成A,B,C,D。以后题目从接口返回只需要把questions数组替换成fetch的结果这个静态模板就变成了一个真正的前端考试页面。第三个技巧是手机端按钮触达区不要小于 44px把.question label的min-height: 44px加上同时用align-items: center让文字垂直居中误触率会明显下降。把模板交出去之前顺手把这三件事做干净。本文还有配套的精品资源点击获取