JavaScript 表单验证 (Form Validation) 深度指南
表单验证是 Web 开发中至关重要的一环。它不仅能提升用户体验(即时反馈),还能减轻服务器压力,但必须记住:前端验证永远不能替代后端验证(安全性)。
本指南将涵盖从 HTML5 原生验证到高级自定义验证的完整方案。
一、验证的两大阵营
1. HTML5 原生验证 (Constraint Validation API)
利用浏览器内置能力,无需写 JS 即可实现基础验证。
- 优点:代码少、性能好、自动支持无障碍 (A11y)、自动显示浏览器默认提示。
- 缺点:样式定制困难、逻辑复杂时难以维护、提示信息不够灵活。
2. JavaScript 自定义验证
完全由 JS 控制验证逻辑和 UI 反馈。
- 优点:逻辑灵活、UI 完全可控、可集成复杂规则(如密码强度、实时比对)。
- 缺点:代码量大、需处理浏览器兼容性、需手动处理无障碍。
最佳实践:混合使用。利用 HTML5 处理基础规则(必填、格式),利用 JS 处理复杂逻辑和自定义 UI。
二、HTML5 原生验证 (Constraint Validation)
1. 常用属性
| 属性 | 作用 | 示例 |
|---|---|---|
required | 必填项 | <input required> |
type | 类型检查 | email, url, number, tel |
pattern | 正则匹配 | <input pattern="[A-Za-z]{3}"> |
min / max | 数值/日期范围 | <input type="number" min="1" max="100"> |
minlength / maxlength | 长度限制 | <input minlength="6"> |
step | 步长 (数字/日期) | <input type="number" step="0.01"> |
2. 核心 API (Constraint Validation API)
| 属性/方法 | 描述 |
|---|---|
element.validity | 返回 ValidityState 对象,包含详细状态 |
element.validationMessage | 浏览器默认的报错信息 |
element.checkValidity() | 返回 true (有效) 或 false (无效) |
element.reportValidity() | 触发浏览器默认提示 UI (气泡) |
element.setCustomValidity(msg) | 设置自定义错误 (非空字符串即视为无效) |
3. ValidityState 对象详解
当 validity.valid 为 false 时,可以通过以下属性判断具体原因:
| 属性 | 含义 | 触发条件 |
|---|---|---|
valueMissing | 值缺失 | required 且为空 |
typeMismatch | 类型不匹配 | type="email" 但格式不对 |
tooShort / tooLong | 长度越界 | 超出 minlength/maxlength |
rangeUnderflow | 低于最小值 | < min |
rangeOverflow | 高于最大值 | > max |
patternMismatch | 正则不匹配 | pattern 不匹配 |
stepMismatch | 步长不匹配 | step 不匹配 |
badInput | 输入无效 | 用户输入了类型不允许的值 (如数字框输入字母) |
customError | 自定义错误 | 调用了 setCustomValidity("msg") |
4. 实战:原生验证 + 自定义提示
<form id="regForm">
<div class="form-group">
<label>邮箱:</label>
<input type="email" id="email" required>
<span class="error-msg"></span>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" id="password" minlength="8" required>
<span class="error-msg"></span>
</div>
<button type="submit">注册</button>
</form>
<style>
.form-group { margin-bottom: 15px; }
.error-msg { color: red; font-size: 12px; display: block; min-height: 18px; }
input:invalid:not(:placeholder-shown) { border-color: red; }
input:valid:not(:placeholder-shown) { border-color: green; }
</style>
<script>
const form = document.getElementById('regForm');
const email = document.getElementById('email');
const password = document.getElementById('password');
// 监听 input 事件,实时验证
[email, password].forEach(input => {
input.addEventListener('input', () => {
const errorSpan = input.nextElementSibling;
if (input.validity.valid) {
errorSpan.textContent = '';
input.setCustomValidity(''); // 清除自定义错误
} else {
// 优先显示自定义错误,否则显示默认错误
if (input.validity.customError) {
errorSpan.textContent = input.validationMessage;
} else if (input.validity.valueMissing) {
errorSpan.textContent = '此项不能为空';
} else if (input.validity.typeMismatch) {
errorSpan.textContent = '请输入有效的邮箱地址';
} else if (input.validity.tooShort) {
errorSpan.textContent = `至少需要 ${input.minLength} 个字符`;
} else {
errorSpan.textContent = input.validationMessage; // 兜底
}
}
});
});
// 提交处理
form.addEventListener('submit', (e) => {
e.preventDefault();
// 触发所有验证并显示默认气泡 (可选)
// form.reportValidity();
if (form.checkValidity()) {
alert('验证通过,提交数据!');
// form.submit();
} else {
// 手动触发所有字段的错误提示
form.reportValidity();
}
});
</script>
三、JavaScript 自定义验证 (高级场景)
当 HTML5 无法满足需求时(如:密码强度、两次密码比对、动态规则),使用 JS 完全控制。
1. 验证规则设计模式
建议将验证规则配置化,便于维护。
const rules = {
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
messages: {
required: '邮箱不能为空',
pattern: '邮箱格式不正确'
}
},
password: {
required: true,
minLength: 8,
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}$/, // 必须包含大小写和数字
messages: {
required: '密码不能为空',
minLength: '密码至少8位',
pattern: '密码必须包含大小写字母和数字'
}
},
confirmPassword: {
required: true,
equalTo: '#password', // 自定义规则:等于另一个字段
messages: {
required: '请确认密码',
equalTo: '两次密码不一致'
}
}
};
2. 通用验证函数
function validateField(field, rules) {
const value = field.value.trim();
const errorSpan = field.nextElementSibling; // 假设错误信息在下一个元素
// 1. 必填检查
if (rules.required && !value) {
showError(field, errorSpan, rules.messages.required);
return false;
}
// 如果非必填且为空,直接通过
if (!value) {
clearError(field, errorSpan);
return true;
}
// 2. 长度检查
if (rules.minLength && value.length < rules.minLength) {
showError(field, errorSpan, rules.messages.minLength);
return false;
}
// 3. 正则检查
if (rules.pattern && !rules.pattern.test(value)) {
showError(field, errorSpan, rules.messages.pattern);
return false;
}
// 4. 自定义规则 (如 equalTo)
if (rules.equalTo) {
const targetValue = document.querySelector(rules.equalTo).value;
if (value !== targetValue) {
showError(field, errorSpan, rules.messages.equalTo);
return false;
}
}
// 通过
clearError(field, errorSpan);
return true;
}
function showError(field, span, msg) {
field.style.borderColor = 'red';
span.textContent = msg;
// 可选:移除 setCustomValidity 避免冲突
field.setCustomValidity(msg);
}
function clearError(field, span) {
field.style.borderColor = '';
span.textContent = '';
field.setCustomValidity('');
}
3. 集成到表单
const form = document.getElementById('regForm');
const fields = {
email: document.getElementById('email'),
password: document.getElementById('password'),
confirmPassword: document.getElementById('confirmPassword')
};
// 实时验证
Object.keys(fields).forEach(key => {
const field = fields[key];
field.addEventListener('blur', () => validateField(field, rules[key]));
field.addEventListener('input', () => {
// 输入时清除错误,提升体验
if (field.style.borderColor === 'red') {
validateField(field, rules[key]);
}
});
});
// 提交验证
form.addEventListener('submit', (e) => {
e.preventDefault();
let isValid = true;
Object.keys(fields).forEach(key => {
if (!validateField(fields[key], rules[key])) {
isValid = false;
}
});
if (isValid) {
alert('提交成功!');
// 发送 AJAX 请求
}
});
四、常见验证场景代码片段
1. 密码强度验证 (实时反馈)
const password = document.getElementById('password');
const strengthBar = document.getElementById('strengthBar');
password.addEventListener('input', () => {
const val = password.value;
let strength = 0;
if (val.length >= 8) strength++;
if (/[a-z]/.test(val)) strength++;
if (/[A-Z]/.test(val)) strength++;
if (/\d/.test(val)) strength++;
if (/[^a-zA-Z0-9]/.test(val)) strength++;
// 更新 UI
const colors = ['red', 'orange', 'yellow', 'lightgreen', 'green'];
const labels = ['弱', '较弱', '中等', '较强', '强'];
strengthBar.style.width = `${(strength / 5) * 100}%`;
strengthBar.style.backgroundColor = colors[strength - 1] || 'red';
strengthBar.textContent = labels[strength - 1] || '';
});
2. 手机号验证 (中国大陆)
function validatePhone(phone) {
const regex = /^1[3-9]\d{9}$/;
return regex.test(phone);
}
3. 身份证验证 (18位)
function validateIDCard(id) {
const regex = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$/;
return regex.test(id);
}
五、用户体验 (UX) 最佳实践
- 实时反馈:
- 在
blur(失去焦点) 时验证,而不是submit时。 - 在
input时清除错误提示,不要让用户一直看到红字。
- 在
- 清晰的错误信息:
- 避免“输入错误”,要说“邮箱格式不正确,请包含 @ 符号”。
- 错误信息颜色使用红色,位置在输入框下方。
- 不要过度验证:
- 用户还没输完不要报错(例如密码长度)。
- 避免在输入过程中频繁弹出浏览器默认气泡。
- 无障碍 (A11y):
- 使用
aria-invalid="true"标记无效字段。 - 使用
aria-describedby关联错误信息,让屏幕阅读器读取。
<input id="email" aria-invalid="true" aria-describedby="email-error"> <span id="email-error" role="alert">邮箱格式错误</span> - 使用
- 防止重复提交:
- 提交时禁用按钮,防止用户多次点击。
六、安全警告:后端验证是必须的!
永远不要信任前端验证!
- 用户可以禁用 JavaScript。
- 用户可以修改 HTML 属性。
- 用户可以直接通过 API 发送请求。
后端验证流程:
- 接收数据。
- 再次执行所有验证规则(长度、格式、业务逻辑)。
- 如果验证失败,返回错误信息给前端。
- 如果验证通过,处理业务逻辑。
七、常用验证库 (可选)
如果项目复杂,手写验证逻辑太繁琐,可以使用成熟库:
- VeeValidate (Vue 生态首选)
- Formik + Yup (React 生态首选)
- jQuery Validate (老旧项目)
- Parsley.js (轻量级)
八、总结
| 场景 | 推荐方案 |
|---|---|
| 基础验证 (必填、邮箱、数字) | HTML5 required, type, pattern + reportValidity() |
| 自定义错误提示 | setCustomValidity() + 自定义 UI |
| 复杂逻辑 (密码强度、比对) | JavaScript 自定义验证函数 |
| 实时反馈 | input / blur 事件监听 |
| 安全性 | 必须在后端再次验证 |
核心公式:
HTML5 基础规则 + JS 自定义逻辑 + 实时 UX 反馈 + 后端安全验证 = 完美的表单验证

857

被折叠的 条评论
为什么被折叠?



