不能直接当步骤条用——它仅提供静态编号语义,无状态控制、校验联动或DOM切换能力;硬套会导致键盘焦点卡死、屏幕阅读器误读、后端校验脱节,必须由外层容器+JS状态管理协同驱动。

<ol></ol> 不能当步骤条用——它只负责编号语义,不控制状态、不响应校验、不切换内容。硬套会导致键盘焦点卡死、屏幕阅读器误读、后端数据校验脱节。
为什么<ol></ol>直接用在多步流程里会出问题
浏览器和辅助技术把 <ol></ol> 当作静态顺序容器,不会管你当前在哪步、上一步有没有填对、第三步按钮点了要不要拦住。常见翻车点:
- 用 CSS 隐藏非当前
<li>,但没给对应<section></section>加aria-hidden="true"和tabindex="-1",键盘用户仍能 tab 进去 - 点击“下一步”时,
<ol></ol>不触发任何拦截,JS 校验写在按钮里又容易漏掉或重复执行 - 用户手动改 DOM 或禁用 JS,
<ol></ol>的 “已完成” 视觉状态完全不可信,后端若不校验step_completed数组,就可能收空数据或跳步提交
真正该用的结构组合
视觉层可用 <ol class="steps"></ol>,但整个流程必须由外层容器驱动:
- 外层用
<div class="wizard"> 包住全部逻辑,不依赖原生表单提交 <li>步骤条用 <code><ol class="steps"></ol>,每个<li data-step="2">只映射状态,不承载交互 - 每步内容用独立
<section data-step="2" aria-hidden="false"></section>,显隐靠 JS 切换aria-hidden和tabindex - 所有按钮统一用
type="button",仅最后提交按钮保留type="submit"并配event.preventDefault() -
validateStep1()只检查email字段是否符合required和type="email",不碰密码字段 -
validateStep2()只校验password和confirm_password是否匹配、长度 ≥8 - 切换逻辑写成:
if (currentStep === 1 && validateStep1()) { updateStep(2); } -
updateStep()函数负责更新data-step类、aria-hidden、焦点管理,不掺校验逻辑 - 前端每次切换步骤,向后端发轻量请求(如
POST /api/step?step=2) - 后端记录 session 中的
step_completed = [1],拒绝任何跳步请求(比如 step=3 但 2 没标记完成) - 最终提交时,后端必须校验
step_completed包含全部必要步骤,不能只信前端传来的字段
校验函数怎么和 currentStep 绑定
核心是「校验先行,状态后置」:每次切换前必须跑对应校验,只在校验返回 true 才更新状态。
立即学习“前端免费学习笔记(深入)”;
前后端怎么防绕过
前端 JS 可被禁用、DOM 可被手动修改,所以后端必须校验请求中携带的步骤上下文:
最容易被忽略的是:步骤条的视觉进度和实际数据有效性之间没有自动绑定关系,必须靠双端协同校验来兜底。漏掉任一端,都等于把验证权交给了用户浏览器。



















