blur事件最可靠,应在input上单独绑定,触发时先清空setcustomvalidity再按规则设错,提交时需用form.reportvalidity兜底校验。

blur 事件比 onchange 更可靠,别用错
失去焦点就该用 blur,不是 change,也不是 focusout。用户点到别处、按 Tab 键、甚至用鼠标点击页面空白处,blur 都能稳稳触发一次;而 change 要求值“真变了”才触发,用户没改内容就移开焦点,校验就彻底漏掉;focusout 会冒泡,父容器加了监听可能拦截或重复执行。
常见错误现象:onchange 绑定后,用户填完邮箱不修改直接点提交按钮,校验函数根本没跑;或者用 focusout 导致同一个输入框连续触发两次校验。
-
blur是语义最准、兼容性最好(IE9+ 全支持)、行为最确定的事件 - 每个
<input>单独绑定addEventListener('blur', handler),别用事件委托——你需要读取当前元素的value和dataset.rule等属性 - 如果表单还支持回车提交,额外监听
keydown并判断event.key === 'Enter',再手动调用校验逻辑
用 setCustomValidity() 实现原生提示,不是弹 alert
别写 alert() 或手动插 DOM 提示,浏览器自带的校验反馈更一致:红色边框、tooltip、阻止 submit。关键就两行:input.setCustomValidity('') 表示通过,input.setCustomValidity('手机号格式不对') 表示失败。
容易踩的坑是只设错不设对:用户第一次输错,你调了 setCustomValidity('...');但第二次改对了,却忘了调 setCustomValidity(''),导致输入框一直标红、无法提交。
- 每次 blur 触发时,必须先清空旧状态:
input.setCustomValidity('') - 再根据规则判断是否出错,出错才设非空字符串
-
required、type="email"这些原生属性依然生效,setCustomValidity()是叠加增强,不是替代 - 需要手动触发全量校验(比如点击提交按钮时),调
form.reportValidity(),它会遍历所有字段并显示错误
onblur 属性能用,但不推荐直接写在 HTML 里
onblur="validateEmail()" 这种内联写法确实能跑,但维护性差、作用域混乱、没法传参、也不方便做 cleanup。现代写法一律用 JS 绑定:
const emailInput = document.getElementById('email');
emailInput.addEventListener('blur', () => {
const value = emailInput.value.trim();
if (!value || !value.includes('@')) {
emailInput.setCustomValidity('请输入有效邮箱');
} else {
emailInput.setCustomValidity('');
}
});
注意:如果你用了防抖(比如校验要发请求查用户名是否已存在),blur 本身不需要防抖——它是离散事件,用户点一下才触发一次;强行加 setTimeout 反而会让校验延迟、this 指向丢失、甚至和后续输入冲突。
- 纯前端校验(长度、正则、必填)必须同步执行
- 涉及网络请求的场景,要用
fetch+ loading 状态控制 UI,而不是靠防抖延后 - 如果真要延迟(极少见),确保闭包捕获了原始
input元素,避免异步回调里访问不到
禁用提交按钮前,先确认 blur 是否已覆盖所有入口
很多人加个 disabled 防重复提交,却忘了 blur 只管“离开输入框”,不管“点击提交按钮”或“按 Enter”。用户可能跳过所有输入框直接点提交,这时 blur 根本没触发,错误状态还是空的。
所以禁用按钮的逻辑不能只依赖 blur 结果,得配合 submit 事件兜底:
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity(); // 强制显示所有未通过项
}
});
真正容易被忽略的是:blur 不等于“已校验完成”。它只是用户交互的一个节点,不是校验终点。提交前那一瞬,才是唯一可信的校验时机。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











