必须监听form submit事件并在第一行同步执行preventDefault(),否则回车等提交方式会漏掉;需手动调用checkValidity()或reportValidity()触发校验,禁用按钮仅作防重复补充而非替代方案。

必须监听 form 的 submit 事件,且 preventDefault() 要放第一行
表单所有提交路径——点击 type="submit" 按钮、回车键、甚至 JS 调用 form.submit()——最终都会触发 submit 事件。只监听按钮 click 或写内联 onsubmit="return false" 都会漏掉回车等场景。
关键点是:event.preventDefault() 必须在事件回调函数的第一行同步执行,不能包在 if 判断里,也不能塞进 setTimeout 或 fetch().then() 中。
- ✅ 正确写法:
form.addEventListener('submit', e => { e.preventDefault(); // 第一行就调 if (!form.checkValidity()) return; // 后续提交逻辑 }); - ❌ 错误写法:
setTimeout(() => e.preventDefault(), 0)、if (valid) e.preventDefault()、或把preventDefault()放在异步校验之后 - 浏览器不会等你异步完成再决定是否提交;一旦事件流走完,提交就已发生
原生校验不自动触发,得手动调 checkValidity() 或 reportValidity()
加了 required、type="email" 这些属性,不代表用户一碰提交就校验。浏览器只在两种情况下触发:用户点击提交(或回车),或你显式调用 form.checkValidity() / form.reportValidity()。
单纯 preventDefault() 只是拦住刷新,不会运行任何校验逻辑——后端照样收到空数据。
立即学习“前端免费学习笔记(深入)”;
-
checkValidity()返回true/false,不提示用户 -
reportValidity()会弹出浏览器默认错误气泡,体验更友好,推荐优先用 - 自定义校验(比如手机号格式)必须配合
input.setCustomValidity("提示文字"),否则checkValidity()永远返回true
禁用提交按钮不是替代方案,而是补充手段
用 button.disabled = true 控制按钮状态,能防止用户连点,但它不解决“绕过按钮也能提交”的问题(比如回车)。而且 disabled 的按钮值不会随表单提交,如果它绑着隐藏字段或动态生成的数据,反而会丢值。
它适合做“防抖+视觉反馈”,但不能代替 submit 事件拦截和校验逻辑。
- ✅ 合理用法:校验通过后启用按钮,或提交中设为
disabled防重复 - ❌ 错误用法:只禁用按钮,却不监听
submit事件——回车仍可提交 - 注意:
fieldset可以整体禁用一组控件,但form标签本身不支持disabled属性
novalidate 属性关的是浏览器自动喊停,不是关校验本身
加了 novalidate,只是让浏览器跳过原生 required、type 等校验,不弹气泡、不阻断提交。但它不影响你自己的 JS 校验逻辑,也不影响 checkValidity() 的执行结果。
很多人加了 novalidate 就以为“不用校验了”,结果所有字段都空着进了后端。
- 加
novalidate的典型场景:你要完全接管 UI 提示(比如用 tooltip 或 red border),不想浏览器气泡干扰样式 - 即便加了它,仍需手动调
form.reportValidity()或自己写正则 +setCustomValidity() -
novalidate是布尔属性,写成novalidate="false"无效,存在即生效
preventDefault() 分开写,中间夹了异步操作,或者误以为禁用按钮就万事大吉。表单提交是个“单向快照”,没机会暂停重试。



















