原生验证需关闭novalidate、正确使用required/type并调用checkvalidity()触发样式更新;仅写required不会自动标红,须在input/blur中调用checkvalidity()刷新:invalid伪类和validity状态。

原生验证不是“能用就行”,而是要关掉 novalidate、正确使用 required 和 type、配合 checkValidity() 触发样式更新,否则红框不出现、错误状态不生效——这是绝大多数人卡住的第一步。
为什么加了 required 却没红框?
HTML5 的验证是“惰性”的:浏览器不会主动渲染 :invalid 样式,除非你显式调用校验方法。只写 <input required> 不会自动标红,也不会更新 aria-invalid。
- 必须在
input或blur事件中调用input.checkValidity(),它才会强制刷新伪类(如:invalid)和validity状态 - 别在
input里调用reportValidity():它会弹出打断式气泡,体验极差 - 移动端中文输入法下,
input事件会在“上屏完成”后触发,符合预期;但若需防抖(比如避免拼音候选阶段误判),可加setTimeout延迟 300ms - CSS 中用
input:invalid:not(:placeholder-shown) { border-color: #dc3545; }控制红框,注意排除 placeholder 状态干扰
如何让错误提示绑定到具体字段而非堆在顶部?
把所有错误塞进一个 <div id="error-summary"> 是最常见却最反模式的做法——用户得滚动、比对、猜测哪一项错了。真正的可访问反馈必须字段级绑定。
<ul><li>每个 <code><input> 后紧跟一个预置的 <div class="invalid-feedback">,不要用 JS 动态 <code>appendChild 插入,避免布局跳动
input.setAttribute('aria-invalid', 'true') 并关联 aria-describedby="email-error"
visibility: hidden; height: 0; overflow: hidden,不用 display: none(后者会让读屏器完全忽略)textContent,而不是靠 CSS 类控制显隐——禁用 CSS 的用户仍需感知内容setCustomValidity() 总是不生效?关键就两步
setCustomValidity() 只管理状态,不触发 UI。它像一个开关,但没人按“确认”按钮,灯就不会亮。
- 设错误:
input.setCustomValidity('密码需含数字和字母')→checkValidity()返回false - 清状态:必须显式调用
input.setCustomValidity(''),漏掉这步,该字段永远验证失败 - 想弹原生气泡?只能配
reportValidity()(提交拦截用)或表单submit事件(自动触发) - 实时反馈别依赖气泡:用
classList.toggle('is-invalid', !isValid)+ 预置.invalid-feedback更可控
最难的不是写逻辑,而是每次 blur 或 input 开始前,先清理旧状态:移 class、清 textContent、重置 aria-invalid。残留的错误提示比没有提示更误导用户。











