只需引入模块无需额外初始化,便可在onchange时对全部绑定data-rule="规则"
属性的表单元素触发校验。css
除内置的规则(url、email、整数和浮点数等),还容许用户注册新的“规则”(见register方法)。html
容许用户重置“错误信息的展现、errorHander、successHander”等行为(经过reset接口见下文),这样设计是为了更灵活的与第三方css组件融合jquery
在submit时,只需手动调用一个方法,并给该方法指定一个“范围”,便可获得校验结果(范围内的全部“输入”是否全都有效)git
总之,要校验谁就给谁加data-rule,再简易的配置一下规则便可。es6
<input type="text" name="link" placeholder="连接" data-rule="url" data-help="不能为空" required>
import form from './form.js' import $ from 'jquery';
初始化完毕~,以上input在value改变时便可触发内置的url
格式校验。github
必选项,只有包含data-rule属性的表单元素才会被校验。支持两种写法:正则表达式
data-rule="类型"函数
data-rule="类型|表达式"ui
关于第一种写法,全部内置类型(共7种,见下表)都支持这么写。
关于第二种写法,只有“zh,en,number,select”3种类型才支持这种写法。表达式中容许出现3个属性:size、decimal、type(后2个只能在number类型的表达式中出现)this
<input type="text" name="nickname" data-rule="zh|size:6-8"> <input type="text" name="username" data-rule="eh|size:6-8"> <input type="text" name="total" data-rule="number|size:6-8,decimal:2-4,type:-1" >
szie 后面的值容许使用'-'表示范围(6到8位中文),固然更能够只写一个单纯的数字
decimal 是number类型的专属,表示保留几位小数,也能够不写,表示不限制小数
type 是number类型的专属,值只能是'+1或-1',分别表示正负数。也能够不写,
全部类型:
类型 | 说明 |
---|---|
url | 连接,带不带协议头都可,支持ftp(s)和http(s) |
regexp | 正则表达式,把值写在pattern属性上,模块会取这里的值进行校验 |
REG_* | 经过register方法添加自定义的类型 |
邮箱 | |
zh | 中文,支持写法2 |
en | 英文,支持写法2 |
number | 数字,支持写法2,正负数、整数和浮点数 |
特别说明:类型为regexp时,正则表达式须要写在pattern属性上。
<input type="text" name="正则校验" data-rule="regexp" pattern="^\d{1,3}$">
选填项,错误时error wording从这里取
选填项,表示该input
为必填项,不写则容许空值。好比下例容许空值,但非空时会校验输入值是否有效:
<input type="text" name="link" placeholder="连接" data-rule="url" data-help="不能为空">
注册新的数据规则
import form from './form.js' import $ from 'jquery'; //添加两种数据格式 form.register({ REG_PASSWORD: /^[A-Z]\w{5,11}$/, REG_NUMBER: /^\d+\.\d{2}$/ })
type
字符串类型,容许出现warn、errorHandler和successHander。
warn表示重置错误信息的展现方式,默认是以alert方式,能够传入第三方的toptip或者toast插件。
form.reset('warn', $.toptip) //或者 var toast = function(type) { return function(msg) { $.toast(msg, type) } } form.reset('warn', toast('forbidden'))
errorHander 表示校验返回失败时执行的动做,模块内会默认给重置的slot函数传入$input对象
form.reset('errorHandler', ($input)=>{ $input.closest('.form-group').addclass('has-error') })
以上实现了校验失败时,往第一个临近当前input的.form-group元素上添加'has-error'样式。 若是不重置,默认是添加在input上
successHandler 表示校验返回成功时执行的动做。用法与errorHandler相同。
注意:errorHandler和successHandler的重置是对称的,不能只重置一个。由于若是只重置errorHanlder(把.has-error挂到.form-group上),success时,默认是从input上移除.has-error的
slot
函数类型
selector
字符串类型,符合jquery语法的选择器。校验指定选择器内的全部表单元素的输入是否有效,返回true或false
import $ from 'jquery'; /** * * 用法设计 * * ```html * <input type="text" name="link" placeholder="连接" data-rule="url" data-help="不能为空" required> * ``` * * * @data-rule 表示该input应输入的值类型,有两种写法 * 1. data-rule="类型" * - url * - email * - zh * - en * - regexp 当设置该类型时,必须跟上pattern属性,值为正则表达式 * - REG_* 经过register方法注册的自定义类型 * * 2. data-rule="类型|表达式" * 表达式只会出现3种属性:size、decimal、type(后二者只用于描述number类型) * - "en|size:3" 表示只能输入3位英文 * - "zh|size:3-10" 表示容许输入3到10位的中文 * - "number|size:3-10,type:'+1',decimal:2" * + type 表示正数(+1),负数(-1),没有type表示正负数以及0 * + decimal 表示小数点保留几位,没有decimal表示整数 * + size 表示位数,没有表示不限位数 * - "select|size:1-3" 表示容许选中几个,通常用于checkbox的第一个 * * @data-help 报错的wording会从 data-help | placeholder 里得到 * * * @required 选填属性。不加该属性表示该项容许空值(即非必填) */ let defaults = { //支持完整(http://www.a.com)的或者不带协议(www.a.com)的url //而且协议只认http(s)和ftp(s) url: /^((f|ht){1}(tp|tps):\/\/)?([\w-]+\.)+[\w-]+(\/[\w- .\?%&=]*)?/, //@左边只容许数字、字母、'.'和'_' //@右边容许出现@sina.com.cn这样的格式 email: /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, //纯中文 zh: /^[\u4e00-\u9fa5]+$/, //纯英文 en: /^[a-zA-Z]+$/, //纯数字,支持正负数,浮点数,以及"1,300,268"这样的写法 number: /^-?\d+(\.\d+)?$/, //不包含`,~,!,@,#,$,%,^,&,*,(,),+,<,>,?,:,",{,},\,/,;,',[,]的任意字符 // word: /[`~!@#\$%\^\&\*\(\)\+<>\?:"\{\}\\\/;'\[\]]/im, regexp: '', select: '' }; let successClass = 'has-success'; let errorClass = 'has-error'; let patterns = defaults; /** * 展现错误信息 * @param {String} msg [错误信息] * @param {Function} theWay [默认以alert方式,能够经过暴露的reset接口重置] * @return {[type]} [展现错误信息] */ let _warn = function(msg, say = alert) { say(msg) }; let _errorHandler = function($input) { $input.addClass(errorClass).removeClass(successClass); }; let _successHandler = function($input) { $input.removeClass(errorClass).addClass(successClass); }; /** * 校验input * @param {Object} $input [jq对象] * @return {Boolean} [true校验经过,false校验失败] */ let _checkIt = function($input) { //一、'data-rule'值为空,不校验 //二、'data-rule'值为'regxp',但'pattern'为空,不校验 //三、 必填项为空值时,直接提示(再也不进行parseRule) //四、非比填项容许为空值 //五、data-rule和input value都不为空时,无论有没required都得校验 if (!$input.data('rule') || ($input.data('rule') == 'regexp' && !$input.prop('pattern'))) { return false } else if (!$input.val() && $input.prop('required')) { return false } else if (!$input.val() && !$input.prop('required')) { return true } else if ($input.val()) { let regxp = _parseRule($input); return regxp.test($input.val()) ? true : false; } }; /** * 解析data-rule * @param {Object} $input [jq对象] * @return {Object} [返回一个正则表达式对象(若是是'select|3-10'类型的会返回位数)] */ let _parseRule = function($input) { let rule = $input.data('rule').trim().toLowerCase(); let type = rule.match(/^(\w+)(\|[\w|\W]+)?/)[1]; let pattern; //判断type是否存在 》属于哪一种rule类型 》对第二种类型进一步分类解析 if (!patterns.hasOwnProperty(type)) { throw type + ' is not registered' } else if (/^\w+$/.test(rule)) { if (type == 'regexp') { pattern = new RegExp($input.prop('pattern')); } else { pattern = patterns[type]; } } else if (/^\w+\|[\w|\W]+/.test(rule)) { // 'size:1-10,decimal:1-2,type:+1' let exp = rule.slice(type.length + 1); //须要扣除掉'|'' let obj = {}; exp.split(',').forEach((item) => { obj[item.split(':')[0]] = item.split(':')[1] }) if (type == 'number') { if (obj['size'] && !obj.hasOwnProperty('decimal') && !obj.hasOwnProperty('type')) { // /^-?\d{1,3}$/ 限定位数的整数 pattern = new RegExp('^-?\\d{' + obj['size'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '+1' && !obj.hasOwnProperty('decimal')) { // /^\d{1,3}$/ 限定位数的正整数 pattern = new RegExp('^\\d{' + obj['size'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '-1' && !obj.hasOwnProperty('decimal')) { // /^-\d{1,3}$/ 限定位数的负整数 pattern = new RegExp('^-\\d{' + obj['size'].replace('-', ',') + '}$'); } else if (obj['type'] == '+1' && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('decimal')) { // /^\d+$/ 不限定位数的正整数 pattern = new RegExp('^\\d+$'); } else if (obj['type'] == '-1' && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('decimal')) { // /^-\d+$/ 不限定位数的负整数 pattern = new RegExp('^-\\d+$'); } else if (obj['decimal'] && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('type')) { // /^-?\d+\.\d{1,3}$/ 浮点数(正负都可),只定小数点的 pattern = new RegExp('^-?\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (!obj.hasOwnProperty('size') && obj['type'] == '+1' && obj['decimal']) { // /^\d+\.\d{1,3}$/ 正浮点数,只定小数点的 pattern = new RegExp('^\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (!obj.hasOwnProperty('size') && obj['type'] == '-1' && obj['decimal']) { // /^-\d+\.\d{1,3}$/ 负浮点数,只定小数点的 pattern = new RegExp('^-\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (obj['size'] && !obj.hasOwnProperty('type') && obj['decimal']) { // /^-?\d+\.\d{1,3}$/ 浮点数(正负都可),整数部分和小数部分均限定 pattern = new RegExp('^-?\\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '+1' && obj['decimal']) { // /^\d{1,3}\.\d{1,3}$/ 正浮点数,整数部分和小数部分均限定 pattern = new RegExp('^\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '-1' && obj['decimal']) { // /^-\d{1,3}\.\d{1,3}$/ 负浮点数,整数部分和小数部分均限定 pattern = new RegExp('^-\\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } } else if (type == 'zh') { //砍掉尾巴2位('+$''),而后拼接{form,to} pattern = new RegExp('^[\\u4e00-\\u9fa5]{' + obj['size'].replace('-', ',') + '}$'); } else if (type == 'en') { pattern = new RegExp('^[a-zA-Z]{' + obj['size'].replace('-', ',') + '}$') } else if (type == 'select') { pattern = obj['size']; } } else { throw rule + ' is wrong.'; } return pattern }; // blur校验input value $(document).on('change', ':input[data-rule]', function(event) { if ($(this).type == 'checkbox' || $(this).type == 'radio') return if (_checkIt($(this))) { _successHandler($(this)); } else { _warn($(this).data('help') || $(this).prop('placeholder') || '格式错误'); _errorHandler($(this)); } }) export default { /** * 校验指定范围内的全部表单元素的输入是否有效 * @param {String} selector [description] * @return {Boolean} [返回true或false] */ validate(selector) { let isValid; $(selector).find(':input[data-rule]').each(function() { if (_checkIt($(this))) { _successHandler($(this)); isValid = true; } else { _warn($(this).data('help') || $(this).prop('placeholder') || '格式错误'); _errorHandler($(this)); isValid = false; return false } }) return isValid; }, /** * 注册一个新的校验类型 * @param {Object} object {type:'REG_NAME',pattern:正则表达式} * @return {[type]} 往默认的defaults对象里添加新的键值对 */ register(object) { $.each(object, function(key, val) { if (!/^REG_/i.test(key)) { throw object.type + ' is invalid name.it must start with "REG_"' } //默认属性放后头,确保不会被新注册的覆盖 $.extend(patterns, { [key.toLowerCase()]: val }, defaults); }) }, /** * 暴露出来的重置接口 * @param {String} type ['warn|errorHandler|successHandler',warn表示展现错误信息;errorHander表示错误时执行的操做] * @param {Function} slot [函数或字符串] * @return [重置内部的方法] */ reset(type, slot) { if (type == 'warn') { _warn = slot; } else if (type == 'errorHandler') { _errorHandler = slot; } else if (type == 'successHandler') { _successHandler = slot; } else { throw type + ' is not invalid type' } } }