打造属于你的表单验证工具

ops/2024/11/25 11:34:42/

前端开发中,表单验证是一个常见且重要的需求。传统的表单验证往往导致代码重复、管理困难,尤其在面对复杂的表单时,验证逻辑容易变得混乱。为了提高开发效率和保证数据准确性,我们开发了一个高度自定义的表单验证器,能够应对各种复杂的验证场景,并支持灵活扩展新的验证规则。

一、目标与背景

表单验证是前端开发中常见的需求。很多时候,我们需要验证用户输入的数据是否符合特定规则,例如检查是否为空、是否为合法邮箱、是否符合手机号的格式等。然而,传统的表单验证往往会导致代码重复、管理困难,尤其在面对复杂的表单时,验证逻辑容易变得凌乱。

为了解决这些问题,开发一个通用的表单验证器,它具有以下特点:

  • 简洁易用:只需简单的配置即可进行表单验证。
  • 高度扩展:提供了多种常见的验证规则,并支持自定义规则。
  • 可视化反馈:能够通过添加 CSS 类自动标记出验证失败的表单项,提升用户体验。
  • 灵活处理:支持在验证时进行自定义校验逻辑,满足不同场景的需求。

二、结构和功能

1、构造函数与验证规则

表单验证器的构造函数如下:

javascript">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();
}

在此构造函数中,我们为常见的验证规则(如requiredemailphone等)定义了相应的验证函数,便于后续调用和扩展。

2、身份证号校验方法

身份证号验证是一个常见的需求,为了确保身份证号码的有效性,我们实现了如下校验逻辑:

javascript">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方法验证每个表单项,并根据字段类型调用相应的规则进行校验:

javascript">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进行逐项验证:

javascript">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方法将表单数据与验证规则结合,为每个字段应用相应的规则:

javascript">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事件监听器,用于移除错误样式:

javascript">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、完整的代码

javascript">// 表单验证构造函数
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) {// 添加classlet 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,并为每个表单字段配置验证规则。以下是一个简单的使用示例:

javascript">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;
}

三、总结

这个表单验证器设计灵活、易于扩展,能够处理常见的验证需求,并支持自定义规则。通过集成错误反馈机制,提升了用户体验。此外,它还为表单字段提供了清晰的验证逻辑,使得开发者可以轻松维护和扩展验证规则。无论你是前端开发新手,还是资深开发者,都能通过这个工具提高开发效率和代码质量。

原文地址


http://www.ppmy.cn/ops/136554.html

相关文章

【漏洞复现】代付微信小程序系统 read.php 任意文件读取漏洞

免责声明: 本文旨在提供有关特定漏洞的信息,以帮助用户了解潜在风险。发布此信息旨在促进网络安全意识和技术进步,并非出于恶意。读者应理解,利用本文提到的漏洞或进行相关测试可能违反法律或服务协议。未经授权访问系统、网络或应用程序可能导致法律责任或严重后果…

oracle排查长时间没提交的事务造成的阻塞案例

一 问题描述 开发同事反馈生产环境某个接口慢&#xff0c;一个普通的按主键更新的update竟然需要5分钟&#xff0c;而我手动执行秒返回&#xff0c;猜测是发生了阻塞&#xff0c;需要排查出阻塞源。 有时&#xff0c;一个事务里会包含多个sql&#xff0c;有的还包含上传附件等…

使用LUKS对Linux磁盘进行加密

前言 本实验用于日常学习用&#xff0c;如需对存有重要数据的磁盘进行操作&#xff0c;请做好数据备份工作。 此实验只是使用LUKS工具的冰山一角&#xff0c;后续还会有更多功能等待探索。 LUKS&#xff08;Linux Unified Key Setup&#xff09;是Linux系统中用于磁盘加密的一…

网页F12:缓存的使用(设值、取值、删除)

一、设置值 例如&#xff1a;设置一个key为name&#xff0c;value为Jack的值 1、在控制台输入代码 localStorage.setItem(name,Jack) 2、查看缓存值 二、取值 1、在控制台输入代码 取出key为name的值 localStorage.getItem(name) 三、删除 删除指定key的值 1、在控制台输…

UE材质不透明蒙版选项

①什么是不透明蒙版 在 Unreal Engine 5 (UE5) 中&#xff0c;不透明蒙版插槽 (Opacity Mask) 是材质中的一个重要参数&#xff0c;用于控制材质的透明度和不透明区域。具体来说&#xff0c;它允许你在材质中实现 部分透明 或 不透明 的效果&#xff0c;通常用于实现如 不规则…

moduo之单例模板Singleton

简介 moduo提供了单例模板类&#xff0c;是线程安全的 结构 单例是动态分配的&#xff0c;不是使用静态变量。其线程安全是通过pthread_once_t #mermaid-svg-N0Vthvvibe8sdg0v {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}…

设计模式——组合实体模式

定义与概念 组合实体模式&#xff08;Composite Entity Pattern&#xff09;是一种设计模式&#xff0c;主要用于将多个相关的实体组合成一个单一的实体&#xff0c;以实现对这些实体的统一管理和操作。这种模式在处理复杂的对象关系时非常有用&#xff0c;它将相关的数据和操…

解决k8s拉取私有镜像401 Unauthorized 问题

拉取镜像时未指定账户和密码通常是因为需要访问的镜像仓库启用了认证&#xff0c;但 Kubernetes 默认配置中未提供访问凭据。要解决此问题&#xff0c;可以按照以下步骤配置镜像仓库的认证信息&#xff1a; 1. 创建 Kubernetes Secret 为镜像仓库配置访问凭据&#xff0c;使用…