应使用 data-status 控制步骤节点状态,因其是单一可信源,可避免 class 切换导致的状态残留,确保跳步、回退及服务端渲染后状态准确,同时支持 css 精准匹配、js 简洁操作、无障碍属性同步和响应式适配。

直接用 data-status 控制步骤节点状态,配合按钮的 onclick 批量更新属性值,比操作 class 更稳、更易维护。
为什么不能靠 class 切换状态
用 classList.add("active") 或 className = "completed" 容易残留旧状态,尤其在跳步、回退或服务端初始渲染后再次 JS 更新时。比如用户从第 4 步直接跳到第 2 步,若只删 active 不清 completed,第 3 步可能仍显示已完成图标。
-
data-status是单一可信源,CSS 用[data-status="active"]精准匹配,无歧义 - JS 只需一行:
stepEl.setAttribute("data-status", "active"),不涉及字符串拼接或 class 拆分 - 服务端返回状态对象如
{ current: 2, completed: [1] },前端可直接遍历设置,无需映射 class 名
按钮点击如何触发步骤状态更新
每个按钮(如“上一步”“下一步”“跳至第3步”)应明确传递目标步骤序号,由统一函数驱动状态重置。
- 按钮写法示例:
<button onclick="goToStep(3)">去第3步</button> - JS 函数内先清空所有
data-status:document.querySelectorAll(".step-item").forEach(el => el.removeAttribute("data-status")) - 再按规则设值:小于目标步的设
data-status="completed",等于的设data-status="active",其余不设(即默认 pending) - 必须校验边界:
if (step totalSteps) return,避免越界后 DOM 属性错乱
HTML 结构必须用 + data-status
步骤条语义和可访问性依赖 <ol></ol>,不能用 <div> 或 <code><ul></ul> 替代;data-status 必须挂载在每个 <li> 上,而非外层容器。
- 正确结构:
<ol class="step-indicator"> <li data-status="completed">填写信息</li> <li data-status="active">确认订单</li> </ol> - 错误写法:
<div class="step-indicator"><div class="step-item active">...</div></div>—— 屏幕阅读器无法识别顺序,aria-current易漏 - 每个
<li>必须加aria-current="step"当且仅当data-status="active",其他状态不加 - CSS 建议直接写:
[data-status="completed"] .step-dot { background: #2196F3; },避免 class 冗余
移动端横竖排切换时状态容易丢失
小屏下步骤条常转为竖排,若用 flex + justify-content: space-between 布局,连接线可能断裂或圆点错位,但状态逻辑本身不受影响——只要 data-status 属性没被 JS 清掉,视觉样式重写即可复原。
- 断点内重置 CSS 即可,例如:
@media (max-width: 768px) { .step-indicator { flex-direction: column; } .step-connector { display: none; } } - 不要在媒体查询里用 JS 重新 setAttribute,那会干扰主流程状态
- iOS Safari 对
flex-basis: 0渲染不稳定,建议用width: calc((100% - 40px) / 3)这类固定计算式替代 - 真正容易丢的是
aria-current—— 每次重设data-status后,务必同步更新aria-current属性
最常被忽略的不是怎么点亮,而是怎么确保“点亮”这个动作在任意跳转、刷新、屏幕阅读器聚焦时都准确反映当前步骤——data-status 是唯一能同时满足 DOM 控制、CSS 驱动和无障碍要求的锚点。











