必须首行调用event.preventDefault(),否则表单默认提交导致页面刷新、JS验证和提示全部失效;它不是可选项,而是接管逻辑的硬性前提。

submit 事件里没写 event.preventDefault() 就别谈提示显示
表单提交后页面刷新、错误提示一闪而过、甚至根本没看到气泡——八成是这行漏了。event.preventDefault() 不是可选项,而是接管验证逻辑的硬性前提。它必须出现在事件处理器第一行,否则验证失败时浏览器已执行默认提交,JS 后续所有提示操作都无效。
常见错误包括:
– 把 preventDefault() 写在 checkValidity() 或异步请求之后
– 在按钮 onclick 里发请求,却没监听 form 的 submit 事件
– 用内联 onsubmit="handle()" 却没在函数末尾 return false
setCustomValidity() 调用后没反应?检查三件事
这个 API 只改元素内部的 validity 状态,不触发 UI、不阻止提交,也不自动更新样式或提示气泡。
-
setCustomValidity("错误信息")表示失败;setCustomValidity("")才代表“通过”——注意:空字符串不是“清除”,而是显式声明“校验通过” - 调用后必须再触发一次验证(如
input.blur()、form.submit()或input.reportValidity()),否则:invalid伪类不会更新 - 多个字段要一起校验,不能只查第一个:
Array.from(form.elements).every(el => el.checkValidity())
后端返回 400 错误,字段级提示塞不进对应 input
问题通常不出在“怎么显示”,而出在“映射错位”和“旧提示残留”。
立即学习“前端免费学习笔记(深入)”;
- 服务端返回的字段名(如
{"email": ["已被注册"]})必须和前端input[name]完全一致(区分大小写、下划线/驼峰) - 每个
input后应紧跟一个固定span.error-message容器,不要依赖全局 toast - 渲染新错误前,先清空所有旧提示:
form.querySelectorAll(".error-message").forEach(el => el.remove()) - 校验失败后记得调用
input.focus(),否则用户可能不知道哪错了
reportValidity() 在 iOS Safari 上不弹提示?换触发时机
这个方法在桌面端通常可靠,但在 iOS Safari(尤其旧版本)上对调用时机敏感:等用户点完“提交”再查,UI 往往来不及响应。
建议提前介入:
- 在
input或blur事件中主动调用input.checkValidity(),并同步更新aria-invalid和提示文案 - 给错误字段加
aria-invalid="true"和aria-describedby="xxx-error",确保读屏器可感知 - 隐藏字段(
display: none或visibility: hidden)即使校验失败,reportValidity()也不会显示提示
最容易被忽略的是:原生验证提示只在完整 HTML 文档上下文中生效——file:// 协议下可能降级,HTTP 响应头缺失 Content-Type: text/html; charset=utf-8 或没写 <!DOCTYPE html>,都会导致验证静默失效。



















