表单验证是前端交互里最基础也最容易出问题的环节。原生JavaScript不依赖任何库就能完成校验,核心思路是拦截提交动作、读取字段值、按规则判断并反馈。理解浏览器自带的约束验证和手写逻辑如何配合,能少写大量重复代码。

一、浏览器原生验证与JS辅助的关系
现代浏览器提供了基于HTML属性的基础校验能力,例如给input加上required、type="email"、minlength等,提交时若不满足,浏览器会自动拦截并弹出提示。这套机制由约束验证API支撑,对应的ValidityState对象可通过element.validity读取,里面包含valueMissing、typeMismatch等布尔字段,告诉我们具体哪类规则没过。
但原生提示样式和文案在不同浏览器差异很大,且无法做跨字段联动(比如确认密码要和原密码一致)。这时就需要用JavaScript监听submit事件,调用checkValidity()做整体检查,或监听input、blur事件做实时校验。注意调用event.preventDefault()阻止默认提交,才能插入自定义逻辑。
// 基础原生+JS配合示例
const form = document.querySelector('#registerForm');
form.addEventListener('submit', function (event) {
// 浏览器原生校验未通过时checkValidity返回false
if (!form.checkValidity()) {
event.preventDefault();
// 这里可以自定义提示,而不用浏览器默认弹窗
console.log('存在字段未通过原生约束');
}
});
二、用正则表达式做常用字段校验
正则表达式是表单验证的利器,尤其针对邮箱、手机号、用户名等无固定HTML类型的输入。写正则时要平衡严谨与宽松,过于严格会误伤真实用户,过于松散则失去校验意义。下面给出两个常见示例,并说明分组与边界的使用。
邮箱正则不必追求覆盖RFC全部规范,业务中常用简易版:以字母数字开头,包含@和域名点号。手机号在国内通常是1开头加十位数字。把这些逻辑封装成函数,便于复用和单测。注意正则中^和$代表整串匹配,避免部分命中导致误判。
// 邮箱与手机号正则校验函数
function validateEmail(val) {
const re = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+.[A-Za-z]{2,}$/;
return re.test(val);
}
function validatePhone(val) {
const re = /^1[3-9]d{9}$/;
return re.test(val);
}
// 在输入框失焦时校验
document.querySelector('#email').addEventListener('blur', function (e) {
if (!validateEmail(e.target.value)) {
e.target.nextElementSibling.textContent = '邮箱格式不正确';
} else {
e.target.nextElementSibling.textContent = '';
}
});
三、实时反馈与内联错误提示
比起提交后才告诉用户错了,输入过程中给出提示体验更好。可监听input事件做轻量校验,blur事件做完整校验。错误展示建议用字段下方的span元素,通过textContent写入信息,并用CSS控制颜色,不要使用alert,那会阻断线程且难看。
为避免频繁校验造成性能浪费,可对input事件做防抖处理,例如300毫秒内只执行一次。同时维护一个错误状态对象,提交时遍历检查,全部通过才允许请求发送。这样结构清晰,也方便后续接异步查重。
let timer = null;
const inputEl = document.querySelector('#username');
inputEl.addEventListener('input', function (e) {
clearTimeout(timer);
timer = setTimeout(() => {
const val = e.target.value;
if (val.length < 4) {
e.target.nextElementSibling.textContent = '用户名至少4位';
} else {
e.target.nextElementSibling.textContent = '';
}
}, 300);
});
四、防重复提交与异步查重技巧
用户快速双击提交按钮,可能造成重复数据。简单做法是在第一次提交后将按钮disabled置为true,或用一个布尔锁变量控制。如果涉及服务端查重(如用户名是否被占用),可在blur时发请求,并用AbortController取消上一次未完成请求,防止旧响应覆盖新结果。
异步校验要注意竞态,比如用户连续输入张三、李四,若第一次请求慢于第二次,可能错误显示李四可用。通过给每次请求标记序号,只采纳最新序号的响应即可解决。以下示例展示用fetch做查重并安全更新提示。
let checkSeq = 0;
document.querySelector('#username').addEventListener('blur', function (e) {
const val = e.target.value;
const seq = ++checkSeq;
fetch('https://ipipp.com/api/check?u=' + encodeURIComponent(val))
.then(res => res.json())
.then(data => {
if (seq !== checkSeq) return; // 已有更新的校验,丢弃旧结果
e.target.nextElementSibling.textContent = data.exists ? '用户名已被占用' : '';
});
});
五、总结与可维护结构建议
把每种校验规则写成独立函数,表单级逻辑只负责调度,是保持脚本清晰的关键。可定义一个rules配置数组,每项包含字段名、校验函数、错误文案,循环绑定。这样新增字段只改配置,不改流程。
另外,记得在服务器端再次校验,前端脚本只是体验层,不能被信任。配合原生API与合理的自定义提示,不用任何框架也能交付靠谱的表单验证功能。
JavaScriptform_validationregular_expression修改时间:2026-08-04 12:27:28