前言
在学习jquery-validate.js时的一些的记录
正文
在使用jquery-validate.js插件时可以做一些初始化配置
在初始化jquery-validate.js对象的时候,将外部的一些配置和该插件内部的一些默认配置合并在一起,如果有相同的配置,前者覆盖后者(默认)的配置
// Constructor for validator
$.validator = function( options, form ) {this.settings = $.extend( true, {}, $.validator.defaults, options );this.currentForm = form;this.init();
};
rules的格式
标准格式是 key为字符串,value为对象字面直接量
rules : {username: {required: true,minlength: 2}
}
也可以是
key为字符串,value也为特定的字符串(“required”)
rulus: {username: "required"
}
在插件中会将上面格式调整为:{required:true}的形式。从下面代码可以看出对于usernname:”minlength”就不适用了,它会把它变成{minlength:true}这规则明显不合适
// Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}normalizeRule: function( data ) {if ( typeof data === "string" ) {var transformed = {};$.each( data.split( /\s/ ), function() {transformed[ this ] = true;} );data = transformed;}return data;}
jquery-validate.js将这些规则解析后放到rules这个对象用以供校验时访问
插件事件监听处理
在指定的元素上添加事件监听
$( this.currentForm ).on( "focusin.validate focusout.validate keyup.validate",":text, [type='password'], [type='file'], select, textarea, [type='number'], [type='search'], " +"[type='tel'], [type='url'], [type='email'], [type='datetime'], [type='date'], [type='month'], " +"[type='week'], [type='time'], [type='datetime-local'], [type='range'], [type='color'], " +"[type='radio'], [type='checkbox'], [contenteditable], [type='button']", delegate )// Support: Chrome, oldIE// "select" is provided as event.target when clicking a option.on( "click.validate", "select, option, [type='radio'], [type='checkbox']", delegate );
上面的监听事件看起来很奇怪,用空格分隔,外加命名空间,如果不了解on的这些使用方法可以参考Query.on() 函数详解。之前focusin,focusout,keyup都是标准事件,之前一直以为focusin与focusout是自定义的事件,这里需要注意一下。
监听函数处理
function delegate( event ) {// Set form expando on contenteditableif ( !this.form && this.hasAttribute( "contenteditable" ) ) {this.form = $( this ).closest( "form" )[ 0 ];}var validator = $.data( this.form, "validator" ),eventType = "on" + event.type.replace( /^validate/, "" ),settings = validator.settings;if ( settings[ eventType ] && !$( this ).is( settings.ignore ) ) {settings[ eventType ].call( validator, this, event );}}
在插件中的settings放置了事件处理函数(settings[ eventType ].call( validator, this, event );
,也就是在defaults中定义的onfocusin,onfocusout,onkeyup,onclick,highlight,unhighlight事件,因为在defaults中所以可以在外部重写这些事件,做一些定制样式,这点会在最后重新封装一个适合自己的表单校验插件)
自定义事件
现在仔细探究一下这些自定义事件在插件中是如何工作的
先看一下jQuery提供的一个trigger() 方法
trigger() 方法触发被选元素的指定事件类型。
格式:$(selector).trigger(event,[param1,param2,…])
event 必需。规定指定元素要触发的事件。可以使自定义事件(使用 bind() 函数来附加),或者任何标准事件。
[param1,param2,…] 可选。传递到事件处理程序的额外参数。额外的参数对自定义事件特别有用。
注意到了trigger可以触发bind函数绑定的事件(bind现在用on取代),也就是说只要我在on中定义一些自定义的事件,都是可以通过trigger触发
例子-trigger
<!DOCTYPE html>
<html>
<head><title>JQuery-validation demo | Bootstrap</title><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" /><script type="text/javascript" src="js/jquery-1.11.1.js"></script><script type="text/javascript" src="js/jquery.validate.js"></script>
</head>
<body>
<div class="listener"><input type="text" /><p class="validate"></p>
</div>
<script type="text/javascript">$(".listener").on("customizeEvent otherEvent",".validate,[type='text']",function() {alert("complete some logical codes here");})$(".validate").trigger("customizeEvent");$("[type='text']").trigger("customizeEvent");$("[type='text']").trigger("otherEvent");
</script>
</body>
</html>
上面的代码中on的第一个参数有两种事件,使用space隔开(这样两种事件都会绑定指定的事件处理函数),第二个参数指定了可以触发这个自定义事件的一些元素(满足选择器[type=’text’],validate的元素),第三个参数是指定使用trigger触发这些事件时执行的处理函数
在接下来执行事件的触发,从代码中可以看到我选择对两个元素触发了不同的事件。
插件表单submit监听
插件绑定了submit的监听事件(.validate为命名空间),当我们通过$(“form”).submit() 或直接点击type=“submit”(input , button可以指定type=“submit”)触发submit事件时,会执行绑定好的处理函数
例子-绑定submit
this.on( "submit.validate", function( event )
例子-submit处理
// Prevent submit for invalid forms or custom submit handlersif ( validator.cancelSubmit ) {validator.cancelSubmit = false;return handle();}if ( validator.form() ) { //校验表单成功if ( validator.pendingRequest ) {validator.formSubmitted = true;return false;}return handle();} else {validator.focusInvalid();return false;}
cancelSubmit
cancelSubmit 是validator对象的成员属性,当满足选择器”:submit”(input,button 的type为submit或者button没有指定类型多数浏览器会把button当做类型为 submit 的按钮)的按钮触发点击事件时,会查看这个按钮上是否包含class为“cancel”或者有formnovalidate=“formnovalidate”属性,如果按钮存在其中一种,那么就不会进行表单校验直接提交form表单(设置validator.cancelSubmit=true)。
validator.form()
使用validator.form()进行表单元素校验,如果为true,判断validator.pendingRequest是否为true,如果是则不提交form,如果false则执行handle函数(handle执行的是submitHandler()的处理)
submitHandler
插件可以在外部配置submitHandler处理函数,它的意思就是在form表单提交时可以做一些额外的处理,并通过返回true,false来决定表单是否提交。
function handle() { //提交表单var hidden, result;if ( validator.settings.submitHandler ) {if ( validator.submitButton ) {// Insert a hidden input as a replacement for the missing submit buttonhidden = $( "<input type='hidden'/>" ).attr( "name", validator.submitButton.name ).val( $( validator.submitButton ).val() ).appendTo( validator.currentForm );}result = validator.settings.submitHandler.call( validator, validator.currentForm, event );if ( validator.submitButton ) {// And clean up afterwards; thanks to no-block-scope, hidden can be referencedhidden.remove();}if ( result !== undefined ) {return result;}return false;}return true;}
生成一个hidden的input隐藏域,在执行完submitHandler以后移除,没能明白这里的意图。 执行submitHandler后会有一个返回结果(true | false | undefined),如果自定义的submitHandler没有return返回则结果是undefined,这样导致handle()结果为false,表单不会被提交
插件表单校验的规则
/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
url
/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
时间
!/Invalid|NaN/.test( new Date( value ).toString()
电话号码
/^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test( value );
数字
/^\d+$/.test( value )
minlength
minlength: function( value, element, param ) {var length = $.isArray( value ) ? value.length : this.getLength( value, element );return this.optional( element ) || length >= param;},
maxlength
maxlength: function( value, element, param ) {var length = $.isArray( value ) ? value.length : this.getLength( value, element );return this.optional( element ) || length <= param;}
rangelength
rangelength: function( value, element, param ) {var length = $.isArray( value ) ? value.length : this.getLength( value, element );return this.optional( element ) || ( length >= param[ 0 ] && length <= param[ 1 ] );}
equalTo
equalTo: function( value, element, param ) {// Bind to the blur event of the target in order to revalidate whenever the target field is updatedvar target = $( param );if ( this.settings.onfocusout && target.not( ".validate-equalTo-blur" ).length ) {target.addClass( "validate-equalTo-blur" ).on( "blur.validate-equalTo", function() {$( element ).valid();} );}return value === target.val();}
插件校验配置方式
插件配置校验有几种方式
$.extend({},$.validator.classRules( element ),$.validator.attributeRules( element ),$.validator.dataRules( element ),$.validator.staticRules( element )), element );
在class上配置下面几种样式
classRuleSettings: {required: { required: true },email: { email: true },url: { url: true },date: { date: true },dateISO: { dateISO: true },number: { number: true },digits: { digits: true },creditcard: { creditcard: true }}
在属性上配置校验提供的规则
在属性中配置校验规则,如<input type="text" required="true" />
attributeRules方法会将规则添加到插件的rules中,在表单校验时执行rules中的规则校验
使用data-来配置
<input id="temp" type="text" data-rule-require = true />
在插件源码中执行dataRules函数
例子-dataRules
dataRules: function( element ) {var rules = {},$element = $( element ),type = element.getAttribute( "type" ),method, value;for ( method in $.validator.methods ) {value = $element.data( "rule" + method.charAt( 0 ).toUpperCase() + method.substring( 1 ).toLowerCase() );this.normalizeAttributeRule( rules, type, method, value );}return rules;}
在data-xx上的数据可以通过jQuery.data获取。使用data有几点需要注意的地方,第一是data获取的数据是经过解析的数据(如0011 获取到的就是11数字),第二是如上面的data-rule-require = true 通过data获取的name是ruleRequire(去掉- ,首字母大写其余小写连接,每个-之间的单词保持小写))
在name中配置
staticRules方法会获取name配置的规则
staticRules: function( element ) {var rules = {},validator = $.data( element.form, "validator" );if ( validator.settings.rules ) {rules = $.validator.normalizeRule( validator.settings.rules[ element.name ] ) || {};}return rules;}
表单校验
checkForm是校验form表单中的每一个元素,让element.elements与自身的rules对比 返回false(验证失败)后加入errorMap集合
showErrors 显示errorMap集合的错误信息。
showErrors方法包含了如何显示校验成功或失败的信息的代码
this.vaild() 函数返回了size: function() {return this.errorList.length;}
是否为0,如果为0则校验通过,如果不为0则不通过
form: function() {this.checkForm();$.extend( this.submitted, this.errorMap );this.invalid = $.extend( {}, this.errorMap );if ( !this.valid() ) {$( this.currentForm ).triggerHandler( "invalid-form", [ this ] );}this.showErrors();return this.valid();}
在showErrors方法中执行了提供了错误样式的可选方法,this.settings.showErrors(可重写),defaultShowErrors默认显示
源码-showErrors
showErrors: function( errors ) {if ( errors ) {var validator = this;// Add items to error list and map$.extend( this.errorMap, errors );this.errorList = $.map( this.errorMap, function( message, name ) {return {message: message,element: validator.findByName( name )[ 0 ]};} );// Remove items from success listthis.successList = $.grep( this.successList, function( element ) {return !( element.name in errors );} );}if ( this.settings.showErrors ) {this.settings.showErrors.call( this, this.errorMap, this.errorList );} else {this.defaultShowErrors();}}
defaultShowErrors方法执行了检验错误元素的highlight显示,校验成功元素的unhighlight显示。需要注意的是defaultShowErrors方法会让错误提示信息全部显示出来this.addWrapper( this.toShow ).show();
该方法中还执行一个比较重要的方法showLabel,在这个方法中可以重写里面的this.settings.errorPlacement
来实现自定义错误信息显示的样式
定制jquery.validate.js
改造方向
- 校验失败的提示信息
- 校验信息的提示位置
- 浮动提示框
- 鼠标移动到校验失败元素上提示错误信息
1.errorElement:”校验错误面板”包含校验信息需要在外界覆盖
2.自定义校验样式一般重写beginValidate(默认使用插件提供的showErrors方法显示错误信息),easyValidate也重写了showErrors因为默认的showErrors会显示校验错误的信息,即使在beginValidate使用display:none,但是执行到默认的showErrors会使用jQuery.show()显示处理
3.success是在$.validate.setting下,因此可以在外界重写,提供校验成功样式
4.highlight是校验失败元素所执行方法,主要是给边框增加校验失败是的样式,这里是显示红色(样式has-error)
5.unhighlight是校验成功元素所执行方法,和highlight恰恰相反,这里显示绿色(样式has-success)
6.jQuery.extend(jQuery.validator.messages, {}) 重写提示内容,可中文显示提示信息
代码-自定义
/**===============jquery-validate.js自定义样式=======================*/
/*** 说明* 封装的easyValidate提供便利的同时也存在着局限性,在使用easyValidate的时候需要注意几点* 1.每个表单元素需要用一个col-xx-xx包含* 2.radio与checkbox需要在外界包含一个div(class为radio),这是由bootstrap提供能够是两种在布局上看起来更加舒适* 3. .form-horizontal .has-feedback .form-control-feedback {right: 15px;}页面bootstrap样式不要满足上面的样式,不然叉和钩两个图标排列有问题top:0,right:0 因为父容器存在padding-right:15px,所以会和padding-right内边距对齐,form-horizontal写在form表单上* 其他说明* 1.errorElement:校验错误面板包含校验信息需要在外界覆盖* 2.自定义校验样式一般重写beginValidate(默认使用插件提供的showErrors方法显示错误信息),easyValidate也重写了showErrors* 因为默认的showErrors会显示校验错误的信息,即使在beginValidate使用display:none,但是执行到默认的showErrors会使用jQuery.show()显示处理* 3.success是在$.validate.setting下,因此可以在外界重写,提供校验成功样式* 4.highlight是校验失败元素所执行方法,主要是给边框增加校验失败是的样式,这里是显示红色(样式has-error)* 5.unhighlight是校验成功元素所执行方法,和highlight恰恰相反,这里显示绿色(样式has-success)*/
(function($){var easyValidate = function(option) {$$this = new easyValidate.prototype.init();$$this.version = '1.0 '; //插件版本return $$this;}easyValidate.prototype = { //在原型中定义easyForm的封装方法init: function () { //初始化方法,返回init的this对象return this;},beginValidate:function() {jQuery.extend(jQuery.validator.messages, {required: "必选字段",remote: "请修正该字段",email: "请输入正确格式的电子邮件",url: "请输入合法的网址",date: "请输入合法的日期",dateISO: "请输入合法的日期 (ISO).",number: "请输入合法的数字",digits: "只能输入整数",creditcard: "请输入合法的信用卡号",equalTo: "请再次输入相同的值",accept: "请输入拥有合法后缀名的字符串",maxlength: jQuery.validator.format("请输入一个长度最多是 {0} 的字符串"),minlength: jQuery.validator.format("请输入一个长度最少是 {0} 的字符串"),rangelength: jQuery.validator.format("请输入一个长度介于 {0} 和 {1} 之间的字符串"),range: jQuery.validator.format("请输入一个介于 {0} 和 {1} 之间的值"),max: jQuery.validator.format("请输入一个最大为 {0} 的值"),min: jQuery.validator.format("请输入一个最小为 {0} 的值")});$("#signupForm").validate({errorElement: "div",errorPlacement: function ( error, element ) {var validate = this;// Add the `help-block` class to the error elementerror.addClass( "help-block" ); //目的是让校验失败的字体变红 具体查看.has-error .help-blockerror.addClass("validate-error panel arrow arrow-left");// Add `has-feedback` class to the parent div.form-group// in order to add icons to inputselement.parents( "[class*=col-]").first().addClass( "has-feedback" );if ( element.prop( "type" ) === "checkbox" ) {error.insertAfter( element.parent( "label" ) );} else {error.insertAfter( element );}// Add the span element, if doesn't exists, and apply the icon classes to it.if ( !element.next( "span" )[ 0 ] ) {$( "<span class='glyphicon glyphicon-remove form-control-feedback'></span>" ).insertAfter( element );}var tempElement = validate.checkable(element.get(0))? element.parents("."+element.get(0).type) : element; //element是jQuery对象,通过get(0)获取dom本身tempElement.off("mouseover").on("mouseover",function(){if(error.parents(".has-error").length!=0) { //如果没有匹配也是一个Object对象,所以用length!=0来判断error.css("display","block");}});tempElement.off("mouseout").on("mouseout",function(){if(error.parents(".has-error").length!=0) {error.css("display","none");}});},showErrors: function() {var i, elements, error;for ( i = 0; this.errorList[ i ]; i++ ) {error = this.errorList[ i ];if ( this.settings.highlight ) {this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );}this.showLabel( error.element, error.message );}if ( this.errorList.length ) {this.toShow = this.toShow.add( this.containers );}if ( this.settings.success ) {for ( i = 0; this.successList[ i ]; i++ ) {this.showLabel( this.successList[ i ] );}}if ( this.settings.unhighlight ) {for ( i = 0, elements = this.validElements(); elements[ i ]; i++ ) {this.settings.unhighlight.call( this, elements[ i ], this.settings.errorClass, this.settings.validClass );}}this.toHide = this.toHide.not( this.toShow );this.hideErrors();},success: function ( label, element ) {// Add the span element, if doesn't exists, and apply the icon classes to it.if ( !$( element ).next( "span" )[ 0 ] ) {$( "<span class='glyphicon glyphicon-ok form-control-feedback'></span>" ).insertAfter( $( element ) );}},/** 校验失败执行的方法,可以添加失败的边框样式*/highlight: function ( element, errorClass, validClass ) {$( element ).parents( "[class*=col-]").first().addClass( "has-error" ).removeClass( "has-success" );$( element ).next( "span" ).addClass( "glyphicon-remove" ).removeClass( "glyphicon-ok" );},/** 校验成功执行的方法,可以添加校验成功的边框样式*/unhighlight: function ( element, errorClass, validClass ) {//$( element ).parents( "[class*=col-]")可能会过滤出多个祖父级别的元素,取最近一个$( element ).parents( "[class*=col-]").first().addClass( "has-success" ).removeClass( "has-error" );$( element ).next( "span" ).addClass( "glyphicon-ok" ).removeClass( "glyphicon-remove" );/** 校验成功移除错误信息面板,因为校验错误鼠标移动到所在元素并输入正确字符后变成校验成功,鼠标从校验成功之前就一直在该* 元素上,所以还会存在一个空白面板*/$("."+errorClass).css("display","none");}});}}easyValidate.prototype.init.prototype = easyValidate.prototype;window.easyValidate = easyValidate();
}(jQuery));
效果图
使用jquery-validate.js时一些注意的地方
jquery-validate.js只对submit实现监听处理而我自己写的一个针对form提交的插件采用的是jquery-form.js的ajax提交方式。因此需要考虑下面几个问题
生成submit事件
使用$(_this.settings.easyForm).submit();触发submit事件,关于这个jquery.submit()函数需要说明一下:调用几次就会触发submit事件的几次,最后传递到后台的请求只有一次
event.preventDefault();会忽略按钮产生的效果,比如说按钮是type=“submit”,那么就不会自动请求到后台
/** 绑定操作按钮事件,默认是表格的查询按钮*/if(this.settings.operationBtn) {var _this = this;$(this.settings.operationBtn).on("click",function(event){event.preventDefault();event.stopPropagation();$(_this.settings.easyForm).submit();_this.sendRequest2Server();});}
上面代码需要注意使用submit()生成了submit以后会让三方执行submit的处理函数,第一是当前自己的form事件处理函数(如果按钮是type=’submit’),第二是jquery.validate.js的submit处理函数,第三是jquery.form.js的submit处理
我要做的就是让jquery.validate.js和自己的form表单提交的submit不执行,
jquery.validate.js的submit不执行,重写submitHandler,直接返回false
submitHandler: function(form, event) {return false; //校验成功以后不提交
}
使用自己的ajax方法请求到后台(sendRequest2Server,使用的是jquery.form.js),在提交之前使用beforeSubmit 校验数据是否有效
例子-jquery.form.js提交方法
sendRequest2Server:function(extra) { //经过表单校验extra = extra || {};extra = $.extend({},this.settings.data,extra); //因为默认配置也有一个可能由外部传入的额外数据,这里extra和settings.data合并/** beforeSubmit 用来校验表单,将easyForm对象提供回调函数作为上下文,在回调函数直接使用this获取*/ easyAjax.easyAjaxForm.call(this,this.settings.easyForm,this.settings.requestUrl,extra,this.settings.callback,true,this.verifyForm());
}
在jquery.form.js提供的ajaxSubmit中重写beforeSubmit,进行表单校验
例子-校验
verifyForm: function(formData, jform, options) {if(!options.context.easyValidate.isValid()) {return false; //easyAjaxForm不会被执行}//formData是 .serializeArray()返回的数据格式options.context.settings.preData = $.param(formData); //记录这一次成功查询的传递数据//页面还是显示highlight、unhighlight样式,这里移除options.context.easyValidate.resetForm();
}
如果isValid()为false则不提交。如何在自定义插件中判断表单是否有效
在使用$form.validate({})创建jquery-validate对象时将对象赋给this.validate成员属性中,通过这个对象的valid()方法判断表单数据是否有效
例子-表单是否有效
isValid: function() {return this.validate.valid();
}
移除jquery.validate.js的校验样式
如果使用ajax提交包含校验内容的表单,在ajax执行成功以后表单样式中还是存在之前的校验的样式
使用jquery.validate.js提供的resetForm可以移除样式,但是却不能移除重写success,highlight,unhighlight的样式。这里在自定义的插件中定义一个方法来调用jquery.validate.js提供的resetForm并在添加移除success,highlight,unhighlight增加的样式
例子-移除样式
resetForm: function() {//移除jquery-validate插件的原有样式this.validate.resetForm();//移除失败样式this.removeHighLight();//移除成功样式this.removeUnHighLight();//移除其它的样式$(".has-feedback",this.form).removeClass("has-feedback");$("[id$='-error']",this.form).remove(); }
参考资料
jquery.validate.js表单验证