最稳方式是用 fieldset[disabled] 原生控制校验范围:仅禁用直属子级表单控件,自动忽略 required/pattern 校验且不提交其值;legend 必为首子元素,禁用中间 div 包裹,禁用 pointer-events 无效。

用 fieldset[disabled] 控制校验范围,别手写 JS 列表
多步骤表单里,每页只校验当前可见字段——最稳的方式不是监听一堆 input 事件,而是用原生 fieldset 包裹每步内容,并通过 disabled 属性开关校验参与权。浏览器会自动忽略 fieldset[disabled] 下所有直属子控件的 required、pattern 等校验逻辑,也不会把它们塞进 FormData。
注意点:
-
fieldset[disabled]只禁用**直属子级**控件(<input>、<select></select>、<textarea></textarea>、<button></button>),如果中间套了<div>,就失效 <li>别用 <code>style="pointer-events: none"模拟禁用,它不影响提交行为,也不阻止键盘 Tab -
legend必须是fieldset的第一个子元素,否则语义和可访问性出问题 - 当前激活页签设
role="tab"+tabindex="0"+aria-selected="true" - 其他页签设
tabindex="-1"+aria-selected="false" - 对应面板加
role="tabpanel"+aria-labelledby指向页签 ID - 隐藏面板必须设
aria-hidden="true",显示的设aria-hidden="false" - 初始设按钮为
disabled - 监听当前
fieldset内所有input、select、textarea的input和change事件 - 事件回调中调用
fieldset.checkValidity(),结果为true才启用按钮 - 避免用
:invalid做实时样式反馈——用户刚点进来就红边框;改用:user-invalid(Chrome 102+ 支持),只在 blur 后触发 - 遍历
fieldset.elements - 过滤掉
type === 'hidden'、disabled、readonly、tabindex === '-1' - 对每个候选元素调用
element.checkValidity(),找第一个返回false的 - 聚焦前加
element.scrollIntoView({ block: 'nearest' }),防被 sticky header 挡住
切换标签页时,必须同步更新 aria-hidden 和 tabindex
仅靠 display: none 或 visibility: hidden 隐藏非当前页内容,对屏幕阅读器不友好。用户按 Tab 键可能跳进已隐藏的输入框,或完全跳过整个面板。
正确做法是:
“下一步”按钮状态该由谁控制?
别在 JS 里手动维护“当前页所有字段是否 valid”的布尔值。浏览器原生提供了更可靠的依据:fieldset.checkValidity()。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
实操建议:
校验失败后聚焦第一个有效字段,别直接 elements[0].focus()
表单里 form.elements 返回的节点列表包含 type="hidden"、disabled、readonly 和 tabindex="-1" 的元素。直接取第一个并 .focus(),大概率聚焦失败或卡住。
安全聚焦流程:
最易被忽略的是:异步校验(比如手机号查重)失败后,不仅要标红字段,还得手动锁住「下一步」按钮,并在接口返回前禁止重复点击——这个状态管理不在原生机制覆盖范围内,得自己补。










