submit事件中必须首行调用event.preventdefault(),否则表单会立即提交;应使用formdata获取数据并按字典序排序键名;签名等预处理需同步完成,禁用按钮需在click和submit双重监听下实现。

submit 事件里必须调用 event.preventDefault()
不加这句,浏览器会立刻序列化表单并跳转或刷新页面,JS 后续所有参数修改都无效。常见错误是把它写在签名或数据清洗之后,或者漏写——哪怕只差一行位置,整个拦截就失效。
必须确保它出现在事件处理函数最开头,且同步执行:
form.addEventListener('submit', function(e) {
e.preventDefault(); // ✅ 第一行就写
// 后续才是取值、清洗、加密、提交...
});
别用 onsubmit="return false" 混搭内联写法,容易和第三方库冲突,也难调试。
用 FormData 构造数据比手动拼对象更可靠
直接读 input.value 会漏掉 disabled 字段、忽略 type="file" 的二进制内容、无法自动处理多选 select,还容易因空格/零宽字符导致签名不一致。
-
new FormData(form)能原生捕获所有启用字段,包括文件和复选框数组 - 但它的键值顺序依赖 DOM 节点顺序,签名前必须按 key 字典序排序(否则重放校验失败)
- 要过滤空值或 trim,得手动遍历:
for (const [key, value] of formData.entries()),再用.replace(/^[\s\uFEFF\u200B\u00A0]+|[\s\uFEFF\u200B\u00A0]+$/g, '') - 动态插入签名字段:不能靠
<input type="hidden" name="sign">静态写死,必须用formData.append('sign', signValue)
密码加密、时间戳补全等预处理必须同步完成
签名或加密逻辑里如果用了 await 或 fetch 预取 nonce,又没提前缓存,就会被浏览器“抢跑”——表单已发出,JS 还卡在等待响应。
真实可行的做法:
- 服务端时间戳、nonce 等依赖项,在页面加载时就用一次
fetch拉取并存在全局变量里 - 密码哈希用同步算法,如
btoa(sha256(password + salt))(注意:仅作传输混淆,非替代 HTTPS) - 补全字段(如
device_id、submit_time)直接formData.append('submit_time', Date.now()) - 避免正则全局替换大文本、Base64 编码整个 JSON 字符串——耗时超过 10ms 就可能影响体验
防重复提交的按钮禁用逻辑不能只写在 submit 里
用户按回车键、或代码里调用 form.submit() 都不会触发 submit 事件监听器,所以只在这里设 button.disabled = true 是无效的。
正确姿势:
- 监听
button[type="submit"]的click事件,点击即禁用 - 同时在
submit事件开头再检查一遍:if (submitBtn.disabled) return;,双重保险 - 若用
fetch提交,成功后清空表单、恢复按钮;失败则只恢复按钮(别用finally无条件恢复) - 禁用范围要覆盖所有提交控件:
form.querySelectorAll('button[type="submit"], input[type="submit"]')
真正起作用的是服务端的一次性 token 机制,前端禁用只是防止肉眼可见的连点,别把它当防重的主力。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











