打造属于你的表单验证工具
在前端开发中,表单验证是一个常见且重要的需求。传统的表单验证往往导致代码重复、管理困难,尤其在面对复杂的表单时,验证逻辑容易变得混乱。为了提高开发效率和保证数据准确性,我们开发了一个高度自定义的表单验证器,能够应对各种复杂的验证场景,并支持灵活扩展新的验证规则。
一、目标与背景
表单验证是前端开发中常见的需求。很多时候,我们需要验证用户输入的数据是否符合特定规则,例如检查是否为空、是否为合法邮箱、是否符合手机号的格式等。然而,传统的表单验证往往会导致代码重复、管理困难,尤其在面对复杂的表单时,验证逻辑容易变得凌乱。
为了解决这些问题,开发一个通用的表单验证器,它具有以下特点:
- 简洁易用:只需简单的配置即可进行表单验证。
- 高度扩展:提供了多种常见的验证规则,并支持自定义规则。
- 可视化反馈:能够通过添加 CSS 类自动标记出验证失败的表单项,提升用户体验。
- 灵活处理:支持在验证时进行自定义校验逻辑,满足不同场景的需求。
二、结构和功能
1、构造函数与验证规则
表单验证器的构造函数如下:
function FormValidator(formId) {
this.formId = formId;
this.validationRules = {
required: function (value) { /* 规则 */ },
email: function (value) { /* 规则 */ },
phone: function (value) { /* 规则 */ },
landline: function (value) { /* 规则 */ },
idCard: this.validateIDCard,
number: function (value) { /* 规则 */ },
minLength: function (value, length) { /* 规则 */ },
maxLength: function (value, length) { /* 规则 */ },
pattern: function (value, regex) { /* 规则 */ },
custom: function (value, validator) { /* 规则 */ },
};
this.addEventListeners();
}
在此构造函数中,我们为常见的验证规则(如required
、email
、phone
等)定义了相应的验证函数,便于后续调用和扩展。
2、身份证号校验方法
身份证号验证是一个常见的需求,为了确保身份证号码的有效性,我们实现了如下校验逻辑:
FormValidator.prototype.validateIDCard = function (idCard) {
if (idCard.length !== 18) return '不是有效的身份证号码!';
if (!/^[0-9Xx]+$/.test(idCard)) return '身份证号码包含无效字符!';
const factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const checksums = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
for (let i = 0; i < 17; i++) {
sum += parseInt(idCard[i], 10) * factors[i];
}
return checksums[sum % 11] === idCard[17].toUpperCase() ? undefined : '身份证号码校验码不正确!';
};
此方法结合长度、字符、和校验码三个层次进行验证,确保身份证号的准确性。
3、验证每个表单项
我们通过validateField
方法验证每个表单项,并根据字段类型调用相应的规则进行校验:
FormValidator.prototype.validateField = function (field) {
const { name, type, value, minLength, maxLength, pattern, errorMsg, customValidator } = field;
let error;
switch (type) {
case 'required': error = this.validationRules.required(value); break;
case 'email': error = this.validationRules.email(value); break;
case 'phone': error = this.validationRules.phone(value); break;
case 'landline': error = this.validationRules.landline(value); break;
case 'idCard': error = this.validationRules.idCard.call(this, value); break;
case 'number': error = this.validationRules.number(value); break;
case 'text':
if (minLength) error = this.validationRules.minLength(value, minLength);
if (!error && maxLength) error = this.validationRules.maxLength(value, maxLength);
break;
case 'pattern': if (pattern) error = this.validationRules.pattern(value, pattern); break;
case 'custom': if (customValidator) error = this.validationRules.custom(value, customValidator); break;
}
return error ? { name, type, value, errorMsg: errorMsg || error } : undefined;
};
4、校验整个表单
validateForm
方法负责验证整个表单,它会遍历表单数据并调用validateField
进行逐项验证:
FormValidator.prototype.validateForm = function (formData = [], valiRules = [], callback) {
const errors = [];
const fields = this.processFormData(formData, valiRules);
fields.forEach(field => {
const error = this.validateField(field);
if (error) {
document.querySelector(`#${this.formId} [name="${error.name}"]`)?.classList.add('form-item-error');
errors.push(error);
}
});
if (callback && errors.length) callback(errors);
return errors;
};
5、处理表单数据
processFormData
方法将表单数据与验证规则结合,为每个字段应用相应的规则:
FormValidator.prototype.processFormData = function (formData, valiRules) {
return formData.map(item => {
const rule = valiRules.find(attr => attr.name === item.name);
return rule ? { ...item, ...rule } : item;
});
};
6、添加事件监听器
为了提升用户体验,表单验证器还为每个输入字段添加了focus
事件监听器,用于移除错误样式:
FormValidator.prototype.addEventListeners = function () {
const formElement = document.querySelector(`#${this.formId}`);
formElement?.querySelectorAll('input, textarea, select').forEach(input => {
input.addEventListener('focus', e => e.target.classList.remove('form-item-error'));
});
};
7、完整的代码
// 表单验证构造函数
function FormValidator(formId) {
this.formId = formId;
// 在构造函数中初始化实例变量
this.validationRules = {
required: function (value) {
return value ? undefined : '此字段为必填项';
},
email: function (value) {
return /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value) ? undefined : '请输入有效的邮箱地址';
},
phone: function (value) {
return /^1[3-9]\d{9}$/.test(value) ? undefined : '请输入有效的手机号码';
},
landline: function (value) {
return /^(\d{3,4}-)?\d{7,8}$/.test(value) ? undefined : '请输入有效的座机号码';
},
idCard: this.validateIDCard, // 直接指向实例方法
number: function (value) {
return /^\d+$/.test(value) ? undefined : '请输入有效的数字';
},
minLength: function (value, length) {
return value.length >= length ? undefined : `长度至少为${length}个字符`;
},
maxLength: function (value, length) {
return value.length <= length ? undefined : `长度不得超过${length}个字符`;
},
pattern: function (value, regex) {
return regex.test(value) ? undefined : '格式不正确';
},
custom: function (value, validator) {
return validator(value) ? undefined : '自定义验证未通过';
},
};
// 在构造函数中绑定事件监听器
this.addEventListeners();
}
// 身份证号校验方法
FormValidator.prototype.validateIDCard = function (idCard) {
if (idCard.length !== 18) {
return '不是有效的身份证号码!';
}
const validIdCardChars = /^[0-9Xx]+$/;
if (!validIdCardChars.test(idCard)) {
return '身份证号码包含无效字符!';
}
const factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const checksums = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
for (let i = 0; i < 17; i++) {
sum += parseInt(idCard[i], 10) * factors[i];
}
const checkDigit = checksums[sum % 11];
if (checkDigit !== idCard[17].toUpperCase()) {
return '身份证号码校验码不正确!';
}
return undefined;
};
// 校验单个字段方法
FormValidator.prototype.validateField = function (field) {
let error = null;
const { name, type, value, minLength, maxLength, pattern, errorMsg, customValidator } = field;
// 根据字段类型进行校验
switch (type) {
case 'required':
error = this.validationRules.required(value);
break;
case 'email':
error = this.validationRules.email(value);
break;
case 'phone':
error = this.validationRules.phone(value);
break;
case 'landline':
error = this.validationRules.landline(value);
break;
case 'idCard':
error = this.validationRules.idCard.call(this, value); // 显式使用实例方法
break;
case 'number':
error = this.validationRules.number(value);
break;
case 'text':
// 针对 text 类型,处理 minLength 和 maxLength 校验
if (minLength) error = this.validationRules.minLength(value, minLength);
if (!error && maxLength) error = this.validationRules.maxLength(value, maxLength);
break;
case 'pattern':
if (pattern) error = this.validationRules.pattern(value, pattern);
break;
case 'custom':
if (customValidator) error = this.validationRules.custom(value, customValidator);
break;
default:
break;
}
// 如果校验不通过,返回 errorMsg(如果提供),否则返回默认错误信息
const errorMessage = error ? errorMsg || error : undefined;
// 返回带有字段信息的错误对象
return errorMessage ? { name, type, value, errorMsg: errorMessage } : undefined;
};
// 校验整个表单方法
FormValidator.prototype.validateForm = function (formData = [], valiRules = [], callback) {
let errors = [],
fields = this.processFormData(formData, valiRules);
fields.forEach(field => {
const error = this.validateField(field);
if (error) {
// 添加class
let inputElement = document.querySelector(`#${this.formId} [name="${error.name}"]`);
if (inputElement) {
inputElement.classList.add('form-item-error');
}
// 收集所有错误
errors.push(error);
}
});
if (typeof callback === 'function' && errors.length > 0) {
callback(errors);
}
return errors; // 返回包含错误信息的数组
};
// 表单数据处理
FormValidator.prototype.processFormData = function (formData, valiRules) {
// 为 formData 中对应的项添加 valiRules 中的属性
const resultFormData = formData.map(item => {
const attr = valiRules.find(attrItem => attrItem.name === item.name);
if (attr) {
return { ...item, ...attr };
}
return item;
});
return resultFormData;
};
// 为表单项添加事件监听器
FormValidator.prototype.addEventListeners = function () {
const formElement = document.querySelector(`#${this.formId}`);
// 为每个输入字段添加事件监听
if (formElement) {
const inputElements = formElement.querySelectorAll('input, textarea, select');
inputElements.forEach(input => {
input.addEventListener('focus', e => {
e.target.classList.remove('form-item-error'); // 去掉错误样式
});
});
}
};
8、如何使用
使用表单验证器非常简单,只需传入表单 ID,并为每个表单字段配置验证规则。以下是一个简单的使用示例:
const validator = new FormValidator('myForm');
const formData = [
{ name: 'username', value: 'mslion' },
{ name: 'phone', value: '12345678901' },
{ name: 'email', value: 'mslion@example.com' },
{ name: 'text', value: 'mslion' },
{ name: 'landline', value: '08565642157' },
{ name: 'idCard', value: '' },
{ name: 'number', value: '45621' },
{ name: 'pattern', value: 'abc123'},
{ name: 'customField', value: 'CustomTest' },
];
const valiRules = [
{ name: 'username', type: 'required', errorMsg: '请选填写您的姓名' },
{ name: 'phone', type: 'phone', errorMsg: '请输入电话' },
{ name: 'email', type: 'email', errorMsg: '请输入邮箱' },
{ name: 'text', type: 'text', minLength: 5, maxLength: 10, errorMsg: '文本不符合要求' },
{ name: 'landline', type: 'landline', errorMsg: '请输入座机号' },
{ name: 'idCard', type: 'idCard', errorMsg: '请输入身份证号' },
{ name: 'number', type: 'number', errorMsg: '请输入数字' },
{ name: 'pattern', type: 'pattern', pattern: /^[a-z0-9]+$/, errorMsg: '格式不正确' },
{ name: 'customField', type: 'custom', customValidator: value => value.includes('Test'), errorMsg: '自定义验证未通过' },
];
const errors = validator.validateForm(formData, valiRules, (errors) => {
console.log(errors);
});
可添加校验不通过的 class
:
.form-item-error {
color: #bb0000 !important;
border-color: #bb0000 !important;
}
.form-item-error::placeholder {
color: #bb0000 !important;
}
三、总结
这个表单验证器设计灵活、易于扩展,能够处理常见的验证需求,并支持自定义规则。通过集成错误反馈机制,提升了用户体验。此外,它还为表单字段提供了清晰的验证逻辑,使得开发者可以轻松维护和扩展验证规则。无论你是前端开发新手,还是资深开发者,都能通过这个工具提高开发效率和代码质量。
共有 0 条评论