前言
在学习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 contenteditable
if ( !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 handlers
if ( 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 button
hidden = $( "<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 referenced
hidden.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 updated
var 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 list
this.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 element
error.addClass( "help-block" ); //目的是让校验失败的字体变红 具体查看.has-error .help-block
error.addClass("validate-error panel arrow arrow-left");
// Add `has-feedback` class to the parent div.form-group
// in order to add icons to inputs
element.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();
}