表单提交应优先使用浏览器原生校验机制,避免手动拦截;禁用 submit 按钮需同步设置 aria-disabled;务必用 FormData 提交,尤其处理文件;提交逻辑只绑定 form 的 submit 事件,不干预原生行为。

表单提交前就该拦截无效输入
浏览器原生的 required、type="email"、pattern 等属性不是摆设,它们能在提交前触发校验,避免无意义的网络请求。关键在于:这些校验由浏览器在 submit 事件之前执行,失败时会自动阻止表单提交,并聚焦到第一个出错字段。
- 别在
submit事件里手动调用checkValidity()再preventDefault()—— 这是重复劳动,还可能掩盖原生提示样式 -
novalidate属性要慎用:加了它,所有原生校验都会失效,等于主动放弃前端第一道防线 - 自定义错误提示请用
setCustomValidity(),而不是直接改innerHTML;否则会干扰reportValidity()的行为
避免 submit 事件中同步阻塞操作
很多开发者习惯在 submit 回调里写一串同步逻辑:收集数据、拼接 JSON、手动序列化、甚至调用未 await 的 Promise —— 这会导致表单“卡住”,用户点一次提交,界面没反应,反复点击,后端收到多条重复请求。
- 所有异步操作(如
fetch)必须显式event.preventDefault(),且只调用一次 - 禁用提交按钮时,记得同时设置
aria-disabled="true"和视觉状态,防止可访问性断裂 - 不要在
submit里做耗时计算(比如遍历大数组生成签名),应提前完成或移交 Web Worker
用 FormData 替代手拼字符串
直接读取 input.value 拼 URLSearchParams 或 JSON,容易漏掉文件、忽略 disabled 字段、混淆 checkbox 的多值逻辑。而 FormData 对象天然适配表单语义,且与 fetch 集成零成本。
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(form); // 自动包含所有有效控件,含 file 输入
const res = await fetch('/api/submit', {
method: 'POST',
body: data // 不需要 .toString(),也不需要设置 Content-Type
});
});
-
FormData会自动跳过disabled字段,符合表单规范 - 上传文件时,
FormData是唯一能正确构造 multipart/form-data 请求体的方式 - 如果后端只认 JSON,那就先用
Object.fromEntries(data)转对象再JSON.stringify,但注意这会丢失文件内容
submit 按钮本身就有性能陷阱
很多人给 button[type="submit"] 加 onclick,或者监听父容器的点击事件来模拟提交——这绕过了浏览器对表单的原生优化路径,比如回车触发表单提交、formnovalidate 属性失效、甚至影响屏幕阅读器识别。
立即学习“前端免费学习笔记(深入)”;
- 确保提交动作只绑定在
form元素的submit事件上,而不是按钮的click - 按钮不要写
return false或preventDefault()在 onclick 里,这会让表单无法通过键盘回车提交 - 若需区分多个提交按钮(如“保存草稿”和“正式提交”),用
event.submitter.name和.value判断,而不是靠 class 或 data 属性











