前一段时间学习了jquery validate 的使用,还满心欢喜的觉得以后终于可以规范化的去写表单验证了。不然,碰到使用validate 检查相同name 值的问题着实难到我了。
juqery validate 自打设计之初就没想着能够验证表单中多个name 值相同的字段,以至于连官方源码中都有这么一句:
// Select only the first element for each name, and only those with rules specified
if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
return false;
}
Select only the first element for each name, and only those with rules specified
找了半天bug的我此时的心是崩溃的,但是也只能不断找解决办法(得亏我有一颗作死的心)。
经过资料的查阅和源码的解读,这段代码是这样的(位于源码的615-643):
elements: function() {
var validator = this,
rulesCache = {};
// Select all valid inputs inside the form (no submit or reset buttons)
return $( this.currentForm )
.find( "input, select, textarea, [contenteditable]" )
.not( ":submit, :reset, :image, :disabled" )
.not( this.settings.ignore )
.filter( function() {
var name = this.name || $( this ).attr( "name" ); // For contenteditable
if ( !name && validator.settings.debug && window.console ) {
console.error( "%o has no name assigned", this );
}
// Set form expando on contenteditable
if ( this.hasAttribute( "contenteditable" ) ) {
this.form = $( this ).closest( "form" )[ 0 ];
}
// Select only the first element for each name, and only those with rules specified
if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
return false;
}
rulesCache[ name ] = true;
return true;
} );
},
其中有这么一句:
var name = this.name || $( this ).attr( "name" ); // For contenteditable
name等于当前元素的name属性,然后再看会发现底部的rulesCache 是一个缓存,采用一个闭包的形式长驻内存。
// Select only the first element for each name, and only those with rules specified
if ( name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
return false;
}
再回到这个判断,如果当前name已经存在rulesCache 中,则直接返回false,就跳过不检查了。总算是明白那句它头上那行注释的原因了。
那么,接下来的问题就是:怎么解决它?
其实就是把这一句
var name = this.name || $( this ).attr( "name" ); // For contenteditable
改成:
var name = this.id || this.name || $( this ).attr( "name" ); // For contenteditable
优先使用id 来赋值name 就行了。
以为这就完了?还是太天真。到了这一步,成功了一大半,但是并不完美。插件已经能够检测相同name 表单字段,但是,默认的表单聚焦永远都只会出现在相同name 字段的第一个。抓狂啊啊啊。或许你会想直接丢弃表单聚焦,但是我就不!继续跟你作!
再看文档会发现validate 有个配置参数showErrors,通过赋值一个函数来决定检测到表单错误的提示:
showErrors:function(errorMap,errorList) {
if(errorList.length){
errorList[0].element.focus();
}
this.defaultShowErrors();
},
稍微解释一下,函数的两个参数,errorMap是每个对应错误表单字段的提示信息数组,errorList 是带有字段dom值和其他一些信息的数组。所以这里就直接通过取出errorList 中的第一个数据的dom对象,聚焦,完事。
别着急→_→ ,其实还没完,这里外层需要一个if 来判断errorList 的长度是否为1,因为在你输入表单的过程中,validate 会不断调用这个函数,如果你不判断,就是一堆的报错信息。
大功告成,再来说一下最后那句
this.defaultShowErrors();
这句的意思就是调用插件原生的错误显示方式,当然你不喜欢可以去掉,自己随便写。
最后,希望大家在jqeury validate的坑中学到知识。顺便贴上一段不用修改源代码文件的方法:
if($.validator){
$.validator.prototype.elements = function () {
var validator = this,
rulesCache = {};
// Select all valid inputs inside the form (no submit or reset buttons)
return $( this.currentForm )
.find( "input, select, textarea, [contenteditable]" )
.not( ":submit, :reset, :image, :disabled" )
.not( this.settings.ignore )
.filter( function() {
var name = this.id || this.name || $( this ).attr( "name" ); // For contenteditable
if ( !name && validator.settings.debug && window.console ) {
console.error( "%o has no name assigned", this );
}
// Set form expando on contenteditable
if ( this.hasAttribute( "contenteditable" ) ) {
this.form = $( this ).closest( "form" )[ 0 ];
}
// Select only the first element for each name, and only those with rules specified
if (name in rulesCache || !validator.objectLength( $( this ).rules() ) ) {
return false;
}
rulesCache[ name ] = true;
return true;
} );
}
}