纯HTML无法实现条件展示逻辑,必须由JavaScript驱动;HTML仅提供可访问的结构骨架,JS负责精准操作与无障碍支持。

纯 HTML 无法实现条件展示逻辑,所有“根据选择显示/隐藏内容”的行为必须由 JavaScript 驱动;HTML 的作用是提供可预测、可访问的结构骨架,让 JS 能精准定位、安全操作、并被辅助技术正确识别。
为什么不能用 :has() 或纯 CSS 做条件展示
尽管现代浏览器开始支持 :has(),但它在 Safari 15.4+、Chrome 105+ 之后才逐步稳定,且不支持嵌套深度超过一层的复杂判断(比如“当 select[data-type="user"] 值为 "business" 且 input#tax-id 已填写”)。更重要的是::has() 无法同步控制 required 属性、无法触发屏幕阅读器播报变化、也不能阻止用户通过键盘 Tab 进入已隐藏字段——这些都属于无障碍和表单完整性问题。
常见错误现象:
- 用
display: none隐藏字段后,仍能 Tab 进去并输入——因为没设tabindex="-1"且没移除required - 用
visibility: hidden替代display: none——视觉隐藏但占布局空间,且屏幕阅读器仍会读出 - 依赖
class="hidden"切换但没配aria-hidden="true"——语音不会跳过,造成信息干扰
如何用 data- 属性对齐 JS 与 DOM 状态
避免在 JS 中硬编码字符串匹配(如 if (el.value === "individual")),而是靠结构化属性建立映射关系,让逻辑更健壮、易维护。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 触发控件加
data-trigger="user-type",目标字段组加data-target="user-type"和data-value="individual" - JS 初始化时统一查找:
document.querySelectorAll('[data-trigger]')绑定事件,再用querySelectorAll('[data-target][data-value="' + value + '"]')批量操作 - 注意 dataset 大小写转换:HTML 中
data-api-url对应 JS 的el.dataset.apiUrl;含数字开头(如data-2fa-enabled)必须用getAttribute("data-2fa-enabled") - 初始状态必须由服务端渲染输出正确
data-value,否则 JS 初始化阶段要多一次 DOM 查询,容易漏掉aria-hidden或required同步
联动字段的 required 属性必须动态同步
表单提交校验只看当前 DOM 状态。如果字段被 JS 显示出来但 required 属性没设上,用户就能绕过校验直接提交;反之,隐藏了却没移除 required,会导致空提交失败。
关键点:
- 不要用
element.setAttribute("required", "")或.removeAttribute("required")—— 它们只影响 HTML 属性,不改变表单控件的 validity 状态 - 正确做法是操作
element.required = true/false,它会实时更新validity.valueMissing - 多字段联动时,推荐封装一个函数:
toggleRequired(el, shouldRequire),内部统一处理required、aria-required、tabindex和aria-hidden - 提交前务必调用
form.checkValidity(),而不是只依赖 CSS 类或 display 状态
分支流程节点必须独立管理 aria-current 和可见性
线性流程(如订单步骤)可用 <ol>,但异常分支(如“已取消”“支付超时”)不能塞进同一列表——这会让屏幕阅读器误判顺序,也增加 JS 条件判断复杂度。
正确结构:
- 主流程用
<ol>,每个<li>是带tabindex="0"和role="link"的可聚焦项 - 分支节点放在独立的
<ul class="branches">中,初始加aria-hidden="true"和style="display:none" - 后端返回
status: "cancelled"时,JS 移除对应<li data-status="cancelled">的aria-hidden,设aria-current="step",并确保它是唯一具有该属性的节点 - 分支节点的
aria-label必须含上下文,例如aria-label="订单已取消,当前步骤",而非仅"已取消"
最容易被忽略的是服务端首次渲染:初始 HTML 必须包含正确位置的 aria-current="step",否则 JS 初始化阶段就得做一次 DOM 查找+重写,既慢又容易漏掉可访问性关键信息。



















