直接用 required 属性不够用,因其仅支持空值和基础格式校验,无法满足用户名禁特殊字符、密码复杂度、手机号规则、二次确认等业务需求,且错误提示不可定制、触发时机滞后、服务端仍需重复校验。

为什么直接用 required 属性不够用
浏览器原生的 required、type="email" 等属性只能做最基础校验,比如空值或格式粗筛。真实场景中,你常需要:用户名不能含特殊字符、密码需同时含大小写字母和数字、手机号要匹配国内11位规则、两次输入密码必须一致——这些原生属性完全不支持。
更麻烦的是,错误提示无法定制样式,用户提交后才触发,体验割裂;且服务端仍需重复校验,前端只是“友好提示”,不是安全防线。
addEventListener('submit') 是起点,但别在它里面写校验逻辑
很多人把所有验证代码塞进表单的 submit 事件里,结果一提交就卡顿、报错没反馈、甚至阻止了默认行为却没手动 preventDefault(),导致页面刷新丢失数据。
- 校验逻辑应提前绑定到
input或blur事件,实时反馈,降低用户挫败感 -
submit事件只做最终兜底:汇总所有字段状态,统一决定是否放行 - 务必在
submit回调开头加event.preventDefault(),否则表单照常提交,脚本校验形同虚设 - 避免在
submit中调用异步校验(如用户名是否已存在),那得改用async/await+event.preventDefault()+ 手动控制提交时机
用正则和 checkValidity() 混合校验更稳
checkValidity() 能复用浏览器对 required、minlength、pattern 的内置判断,但它的 pattern 只支持字符串字面量正则,不支持标志(如 /i),也不能动态生成。
所以实际做法是:用 pattern 做简单格式(如邮箱、数字),复杂规则(如“至少一个大写字母+一个数字”)用 JS 正则单独判断,并手动设置 setCustomValidity()。
const pwdInput = document.querySelector('#password');
pwdInput.addEventListener('input', () => {
const value = pwdInput.value;
const hasUpper = /[A-Z]/.test(value);
const hasDigit = /\d/.test(value);
if (value && (!hasUpper || !hasDigit)) {
pwdInput.setCustomValidity('密码需包含大写字母和数字');
} else {
pwdInput.setCustomValidity('');
}
});
注意:setCustomValidity('') 必须显式调用,否则一旦设过非空字符串,后续即使输入合法也不会自动清空错误状态。
提交前批量收集错误并聚焦第一个问题字段
用户填完点提交,如果所有错误一起弹出,反而不知从哪改起。更好的做法是:遍历所有带校验的字段,收集 validity.valid === false 的元素,只滚动并聚焦第一个,其余留作后台校验补充。
- 用
form.querySelectorAll('[required], [pattern], [data-validate]')定义校验范围,避免硬编码 ID - 对每个字段调用
checkValidity()后,检查validity.customError、validity.valueMissing等具体原因,方便差异化提示 - 不要用
alert(),改用field.insertAdjacentHTML('afterend', '<small class="error">...</small>')插入提示,再用field.scrollIntoView({ behavior: 'smooth', block: 'center' })
真正难的不是写几行正则,而是让错误提示出现在用户视线里、且不打断操作流——这点容易被忽略,但直接影响表单完成率。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











