结论:原生 disabled/required 控制验证流程更轻、更稳、更无障碍友好;fieldset[disabled]仅递归禁用直属子级表单控件,需确保控件直接嵌套在 fieldset 内而非包裹于 div 等中间层,且 legend 必须为 fieldset 首个子元素以保障语义与可访问性。

直接说结论:用 浏览器原生验证机制不是“备选方案”,而是多级表单验证的主干路径。
为什么不用 JS 监听每个 input 再手动判断?
因为容易漏掉边界情况:form.elements 遍历时会包含 type="hidden"、disabled、readonly 元素,直接 elements[0].focus() 可能聚焦到不可交互字段上;手动维护“当前步骤有效字段列表”极易和 DOM 状态脱节;而 fieldset[disabled] 是浏览器原生递归禁用逻辑,它自动排除字段参与校验、提交、FormData 收集——不靠 JS 模拟,就不会出错。
fieldset[disabled] 什么时候生效?怎么用才对?
关键只有一条:它只禁用 fieldset 的**直属子级表单控件**(<input>、<select></select>、<textarea></textarea>、<button></button>),不穿透 <div> 或其他包裹层。<ul>
<li>正确:<code><fieldset disabled><input name="city"><select name="district"></select></fieldset> → 两者都被禁用
<fieldset disabled><div><input name="city"></div></fieldset> → input 不受影响fieldset 上用 style="pointer-events: none" 替代 disabled,它不影响提交行为,也不阻止键盘 Tabbutton 的 disabled 渲染有缺陷,可加 style="pointer-events: none;" 保底,但仅作视觉补充如何让“下一步”按钮只在当前组校验通过后才可点?
别靠 JS 手动监听所有字段变化。优先用 CSS 伪类 + 原生状态:
立即学习“前端免费学习笔记(深入)”;
- 初始设按钮为
disabled,并绑定到当前fieldset内所有required字段是否都满足:valid - 避免用
:invalid做样式反馈——用户还没输就红边框,体验差;改用:user-invalid(Chrome 102+ 支持),只在用户 blur 后触发 - 稳妥做法:监听当前
fieldset下input的input事件,调用checkValidity(),再统一更新按钮状态 - 注意:空的
required字段默认是:invalid,但只有 blur 后才真正触发校验提示;checkValidity()能立即返回结果
legend 位置和内容为什么不能马虎?
legend 不是装饰,是屏幕阅读器把整组控件绑定为一个逻辑单元的唯一锚点。一旦写错,整组语义就断裂:
- 必须是
fieldset的第一个子元素,且不能被任何标签包裹(包括<p>、<div>、React Fragment) - 错误写法:
<fieldset><div><legend>地址</legend></div><input name="city"></fieldset>→legend被忽略 - 错误写法:
<fieldset><input name="zip"><legend>地址</legend></fieldset>→ 旧版 Safari 可能不播报 - 正确写法:
<fieldset><legend>收货地址</legend><input name="city"></fieldset>—— 纯文本,无内嵌,无空格占位符 - 视觉隐藏要用
position: absolute; clip: rect(1px, 1px, 1px, 1px);,绝不能用display: none或visibility: hidden
实际复杂点不在怎么写,而在要不要把 legend 当成必需品来对待——很多人删掉它图省事,结果表单在 VoiceOver 下完全不可操作,却以为是 JS 错了。



















