var chars = jQuery.browser.safari && parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",
//包括中英文* _ -\.的任意单个字符
quickChild = new RegExp("^>\\s*(" + chars + "+)"), //匹配以>开始的,后面至少有一个字符的内容,如>child,> child
quickID = new RegExp("^(" + chars + "+)(#)(" + chars + "+)"),//匹配如nodeName#idName
quickClass = new RegExp("^([#.]?)(" + chars + "*)");//匹配如#idName, .className, nodeName
//多重过滤函数,对已有的dom元素进行多重过滤,elems可以是dom元素数组[dom1,dom2..],也可以是jq实例{0:dom1, 1: dom2...}
//多重过滤的意思,也就是expr可以是以,分开的n个过滤表达式如,".a,.b,:odd",not参数为true的话,表示返回除了找到的元素以外的元素
multiFilter: function( expr, elems, not ) {
var old, cur = [];
//old每次过滤前保存表达式字符串,用来与过滤后expr进行比较,如果两者相同,表示过滤循环结束
while ( expr && expr != old ) {
old = expr;
var f = jQuery.filter( expr, elems, not );//其实多重过滤是把以,分开的表达式进行filter函数处理,所以过滤的最核心函数是filter函数
expr = f.t.replace(/^\s*,\s*/, "" );//一次过滤结束后,如果是多重的话,返回肯定是",xxx",所以得把“,”替换掉以进行下一次过滤
cur = not ? elems = f.r : jQuery.merge( cur, f.r );//elems保存返回的dom数组,因为not为true的话,
下一次过滤肯定是从返回的dom数组里进行再一次的过滤,如果not不存在,则用cur和返回数组进行合并,
不改变elems,因为返回只是一次过滤成功的元素,所以elems不变,但这会存在一个问题,
如<div class="a b"></div>如果expr为".a, .b"那么第一次返回数组里有这个dom对象,第二次又会有这个对象,所以得进行unique处理
}
return cur;//而jq这里对最终结果没有处理!所以会出现上面所说的情况,最终返回dom数组里有可能包含同一个dom元素n次。
}
下面听asfman来着重讲解一下jq的过滤核心函数filter函数
//filter函数所用到的正则表达式
jQuery.parse=[
/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,// 匹配如[@value='test'], [@foo]
/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,// 匹配如:contains('foo'),:has('.a'),:nth-child(4n)
new RegExp("^([:.#]*)(" + chars + "+)")//匹配如:even, :last-child, #id, .class, nodeName
]
filter: function(t,r,not) {//t为过滤表达式如".a", ".a.b", "#id","nodeName","[attr=value]",":nth-child(4n)"等等
var last;
while ( t && t != last ) {
last = t;//用来保存上一次过滤钱的表达式,如果last和t相同则结束循环
var p = jQuery.parse, m;
for ( var i = 0; p[i]; i++ ) {//对parse数组进行遍历
m = p[i].exec( t );//m保存匹配结果m[1]为"[",":", ".","#"或者为空
if ( m ) {//如果m存在则停止遍历
t = t.substring( m[0].length );//移除匹配到的表达式如t为".a.b"那么匹配到.a后,t为".b"
m[2] = m[2].replace(/\\/g, "");//如果m[2]中存在\那么就替换掉
break;
}
}
if ( !m )
break;//如果遍历完parse后,m仍旧为null的话,那么此次过滤完全结束,执行while以后的代码
//:not是一个非常特殊的表达式,jq对它进行了优化处理
if ( m[1] == ":" && m[2] == "not" )
r = isSimple.test( m[3] ) ?
//判断m[3],也就是:not(nodeName),
因为isSimple = /^.[^:#\[\.]*$/,它表示除了:#[.以外的任意字符,也就是简单的nodeName或者*,那么进行
jQuery.filter(nodeName, r, true)进行简单过滤,也就是new RegExp("^([:.#]*)(" + chars + "+)")/
会匹配到,然后返回不包含此nodeName的所有元素
jQuery.filter(m[3], r, true).r :
//:not(.a),:not([attr=value])的复杂类型的情况进行jQuery(r).not(m[3])处理
jQuery( r ).not( m[3] );
/*
not: function( selector ) {
if ( selector.constructor == String )
// test special case where just one selector is passed in
if ( isSimple.test( selector ) )
return this.pushStack( jQuery.multiFilter( selector, this, true ) );
else//也就是进行这步处理,以m[3]为.a为例,那么这里返回所有包含.a的dom元素
selector = jQuery.multiFilter( selector, this );
var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType;
return this.filter(function() {//此函数的第一个参数为i,this为dom元素, 如果这个dom元素不在包含.a的dom数组
内,那么返回该元素,也就是说返回所有不包含.a的元素
return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector;
});
}
filter: function( selector ) {
return this.pushStack(
jQuery.isFunction( selector ) &&
jQuery.grep(this, function(elem, i){
return selector.call( elem, i );
}) ||
jQuery.multiFilter( selector, this ) );
}
*/
else if ( m[1] == "." )//如果m[1]为.那么它肯定是过滤.className
r = jQuery.classFilter(r, m[2], not);//r为dom数组,m[2]为className
/*
//对dom数组里的元素进行className的过滤,如m为".a"那么返回所有className包含.a的元素,
如果not为true,那么返回所有className不包含.a的元素。
classFilter: function(r,m,not){//r为dom数组或jq实例,m为className,not表示返回是否包含某个className的元素
m = " " + m + " ";//className前后加空格是为了防止如className为"ab", 这样m为a的话也算包含a了,而通过" a "可以避免这种情况
var tmp = [];//保存返回dom元素的数组
for ( var i = 0; r[i]; i++ ) {//进行遍历处理,判断dom元素是否包含这个class
var pass = (" " + r[i].className + " ").indexOf( m ) >= 0;
if ( !not && pass || not && !pass )//not不存在,并且找到了,或者not为true,没找到的情况临时数组保存该dom元素
tmp.push( r[i] );//这里也可以通过if(!!not^pass)进行异或来判断
}
return tmp;
}
*/
else if ( m[1] == "[" ) {//如果m[1]为[,那么它就是过滤属性的
///^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,// 匹配如[@value='test'], [@foo]
var tmp = [], type = m[3];//m[3]也就是包含!= ^= $= ~=这些
for ( var i = 0, rl = r.length; i < rl; i++ ) {//遍历dom数组
var a = r[i], z = a[ jQuery.props[m[2]] || m[2] ];
//jQuery.props[m[2]]返回真是的属性名字,如果props返回为undefined那么z=a[m[2]],如elem.readOnly
/*
props: {
"for": "htmlFor",
"class": "className",
"float": styleFloat,
cssFloat: styleFloat,
styleFloat: styleFloat,
readonly: "readOnly",
maxlength: "maxLength",
cellspacing: "cellSpacing"
}
*/
if ( z == null || /href|src|selected/.test(m[2]) )
//如果z不存在或者m[2]是href|src|selected中毒一个
z = jQuery.attr(a,m[2]) || '';//通过attr函数再去找看是否是elem.style的属性,存在返回,不存在返回空字符串
if ( (type == "" && !!z ||//如果!= ^= $= ~=不存在并且z存在[name]
type == "=" && z == m[5] ||//如果type为=,z存在,并且z == xxx如[name=xxx]
type == "!=" && z != m[5] ||//如果type为!=,z存在,并且z != xxx如[name!=xxx]
type == "^=" && z && !z.indexOf(m[5]) ||如果type为^=,z存在,并且z中能找到m[5]开始,z.indexOf(xxx)如[name^=xxx]
type == "$=" && z.substr(z.length - m[5].length) == m[5] ||//以m[5]结束
(type == "*=" || type == "~=") && z.indexOf(m[5]) >= 0)//z中能找到m[5]
^ not )//就行异或,not为true则返回以上条件为false的元素,not不存在则返回以上结果为true的元素
tmp.push( a );
}
r = tmp;//循环结果r保存过滤到的元素数组
} else if ( m[1] == ":" && m[2] == "nth-child" ) {///^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,// 匹配:nth-child(4n)
var merge = {}, tmp = [],//merge用来避免取到相同元素
test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(//匹配 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'等
m[3] == "even" && "2n" || m[3] == "odd" && "2n+1" ||//如:nth-child(even)则even变为2n,odd为2n+1
!/\D/.test(m[3]) && "0n+" + m[3] || m[3]),//如果为nth-child(3)则3变为0n+3
first = (test[1] + (test[2] || 1)) - 0, last = test[3] - 0;//如2n+1那么first为2,last为1,如0n+3那么first为0,last为3
//如果test[2]为0的话,因为这里0是个字符串所以"0"||1仍旧返回0,只有当test[2]不存在的情况才为1,也就是如n,n+1这种
//遍历dom数组
for ( var i = 0, rl = r.length; i < rl; i++ ) {
var node = r[i], parentNode = node.parentNode, id = jQuery.data(parentNode);
//node保存dom元素,parentNode保存dom元素的父元素,id为该父元素的expando属性的值
if ( !merge[id] ) {//判断此父元素是否被遍历,防止重复遍历。
var c = 1;/
for ( var n = parentNode.firstChild; n; n = n.nextSibling )
if ( n.nodeType == 1 )
n.nodeIndex = c++;
//对每个元素进行处理,用nodeIndex来保存元素所在父元素的位置,如第一个则nodeIndex为1
merge[id] = true;
}
var add = false;//add用来表示是否满足条件
if ( first == 0 ) {//如果first为0也就是:nth(3)这种情况
if ( node.nodeIndex == last )//如果当前元素的nodeIndex等于last 则add为true
add = true;
} else if ( (node.nodeIndex - last) % first == 0 && (node.nodeIndex - last) / first >= 0 )
//如2n+1,那么当nodeIndex为3-1%2 == 0时,add为true,如2n+3, 1 - 3 / 2 == -1是不成立的
add = true;
if ( add ^ not )//进行异或,把满足条件的元素推进tmp数组
tmp.push( node );
}
r = tmp;
//除了:not(.a), .className, [name = xxx] :nth-child(2n+1),这些情况外的进行以下处理
} else {
var fn = jQuery.expr[ m[1] ];//m[1]可以为"", ":", "#",fn也就是jQuery.expr[""],jQuery.expr["#"],jQuery.expr[":"]
/*
expr: {
//new RegExp("^([:.#]*)(" + chars + "+)")//匹配如 m[1]为空,m[2]为nodeName或者"*"
"": function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},
//a为elem, i为index, m为匹配到的数组, r为需要过滤的数组,如果m[2]为*,或者a的nodeName为m[2]
//如filter("div", r), filter("*",r)
"#": function(a,i,m){return a.getAttribute("id")==m[2];}//m[2]为id的值
}
*/
if ( typeof fn == "object" )
fn = fn[ m[2] ];
//如果fn为对象也就是jQuery.expr[":"]这种情况,fn为jQuery.expr[":"][m[2]]
,如jQuery.expr[":"]["first-child"]=function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},
所以我们可以很容易对expre[":"]进行扩展,如jQuery.expr[":"].fk = function(elem, i, m){return elem.fk == m[3];}
:fk(you)匹配<div fk="you"></div>
if ( typeof fn == "string" )//如果fn为字符串,如jQuery.expr[":"].name ="a.name == arguments[3][3]"
fn = eval("false||function(a,i){return " + fn + ";}");
r = jQuery.grep( r, function(elem, i){//通过grep来得到符合条件的元素
return fn(elem, i, m, r);
}, not );
}
}
return { r: r, t: t };
//返回过滤后的dom数组,和截取后的t,如t = t.substring( m[0].length );//移除匹配到的表达式如t为".a .b"那么匹配到.a后,t为" .b"
}
posted on 2008-10-27 09:38
汪杰 阅读(795)
评论(0) 编辑 收藏 引用