还漏了一个框题,jQuery的冲突机制解决方法jQuery.noConflict()以及jQuery.noConflict(extreme),这里先分析一下:
jQuery.noConflict():运行这个函数将变量$的控制权让渡给第一个实现它的那个库。
jQuery.noConflict(extreme):将$和jQuery的控制权都交还给原来的库。
比如在prototype框架中的$会和jQuery框架中的$产生命名冲突,这里就是为了解决这种问题。
现在先看下noConflict方法的具体实现:
noConflict: function ( deep ) { window.$ = _$; if ( deep ) window.jQuery = _jQuery; return jQuery; }其中_$,_jQuery是在jQquery源码的开始几行定义的:
( function (){ var // _jQuery = window.jQuery, _$ = window.$, // })();他们都是为了防止$被覆盖而将window.jQuery,window.$放在临时变量中保存起来。
当deep为空的时候,“_$”覆盖“window.$”,“$”的常规功能失效,但jQuery还可以继续使用。当有新的库中重新定义“$”的时候,“jQuery”继续为jQquery的常规功能,而“$”就不是jQuery中的了,它是属于新的库的常规功能;
当deep不为空的时候,它将“_jQuery”覆盖“window.jQuery”,这样导致可能jQuery插件失效;另外方法返回的jQuery,实际上没有被覆盖。通过它完全可以移到新的一个命名空间,如dom.query = jQuery.noConflict(true); dom.query("div p").hide();
前言
这篇文章将介绍jQuery选择器的原理,主要内容包括:
分析
一、基本
1. 【#id】和【element】
在第一篇中曾经提到核心函数的概念,形如$("#result")【jQuery(expression,[context])】表达式,归根调用【jQuery(elements)】,因此将调用:
if ( selector.nodeType ) { this [ 0 ] = selector; this .length = 1 ; this .context = selector; return this ; }
2. 【.class】
在第一篇中曾经提到核心函数的概念,形如 $(".container") 【jQuery(expression,[context])】表达式字符串的实现。
3. 【*】
通过第一篇的结论,将代码进行到:
Sizzle.find = function (expr, context, isXML){ var set, match; if ( ! expr ) { return []; } for ( var i = 0 , l = Expr.order.length; i < l; i ++ ) { var type = Expr.order[i], match; if ( (match = Expr.match[ type ].exec( expr )) ) { var left = RegExp.leftContext; if ( left.substr( left.length - 1 ) !== " "" " ) { match[ 1 ] = (match[ 1 ] || "" ).replace( / "" / g, "" ); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break ; } } } } if ( ! set ) { set = context.getElementsByTagName( " * " ); } return {set: set, expr: expr}; };当expr为“*”的时候,根据Expr.match[ type ].exec( expr )为true时,type为TAG,因此将执行:
if ( left.substr( left.length - 1 ) !== """" ) { match[1] = (match[1] || "").replace(/""/g, ""); set = Expr.find[ type ]( match, context, isXML ); if ( set != null ) { expr = expr.replace( Expr.match[ type ], "" ); break; } }
继续查看Expr.find[ type ]的方法,具体实现如下:
var Expr = Sizzle.selectors = { // find: { ID: function (match, context, isXML){ if ( typeof context.getElementById !== " undefined " && ! isXML ) { var m = context.getElementById(match[ 1 ]); return m ? [m] : []; } }, NAME: function (match, context, isXML){ if ( typeof context.getElementsByName !== " undefined " && ! isXML ) { return context.getElementsByName(match[ 1 ]); } }, TAG: function (match, context){ return context.getElementsByTagName(match[ 1 ]); } }所以将调用context.getElementsByTagName("*");返回context中所有的DOM元素。
4. 【selector1,selector2,selectorN】
参考第一篇中的内容,当表达式包含“,”符号的时候,最后也是返回一个jQuery对象。
二、层级
1. 【ancestor descendant】
在给定的祖先元素下匹配所有的后代元素。
HTML代码jQuery代码结果<form> <label>Name:</label> <input name="name" /> <fieldset> <label>Newsletter:</label> <input name="newsletter" /> </fieldset> </form> <input name="none" /> $("form input")[ <input name="name" />, <input name="newsletter" /> ]
首先“ancestor descendant” 作为一个表达式字符串,根据第一篇中的内容,它将执行:
// 处理 形如 $("div .container")的表达式字符串 else return jQuery( context ).find( selector );接着查看jQuery对象的find方法:
find: function ( selector ) { // 当表达式不包含“,”符号时候 if ( this .length === 1 && ! / , / .test(selector) ) { var ret = this .pushStack( [], " find " , selector ); ret.length = 0 ; jQuery.find( selector, this[0 ], ret ); return ret; } // 当表达式包含“,”符号时候 else { var elems = jQuery.map( this , function (elem){ return jQuery.find( selector, elem ); }); return this .pushStack( / [^+>] [^+>] / .test( selector ) ? jQuery.unique( elems ) : elems, " find " , selector ); } }由于jQuery.find = Sizzle; 因此查看Sizzle对象的具体实现:
Code其中set = Sizzle.filter( ret.expr, ret.set );调用Sizzle.filter方法:
Sizzle.filter = function (expr, set, inplace, not){ var old = expr, result = [], curLoop = set, match, anyFound; while ( expr && set.length ) { for ( var type in Expr.filter ) { if ( (match = Expr.match[ type ].exec( expr )) != null ) { var filter = Expr.filter[ type ], found, item; anyFound = false ; if ( curLoop == result ) { result = []; } if ( Expr.preFilter[ type ] ) { match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not ); if ( ! match ) { anyFound = found = true ; } else if ( match === true ) { continue ; } } if ( match ) { for ( var i = 0 ; (item = curLoop[i]) != null ; i ++ ) { if ( item ) { found = filter( item, match, i, curLoop ); var pass = not ^ !! found; if ( inplace && found != null ) { if ( pass ) { anyFound = true ; } else { curLoop[i] = false ; } } else if ( pass ) { // $("form input")从这里进 result.push( item ); anyFound = true ; } } } } if ( found !== undefined ) { if ( ! inplace ) { curLoop = result; } expr = expr.replace( Expr.match[ type ], "" ); if ( ! anyFound ) { return []; } break ; } } } expr = expr.replace( / "s*,"s* / , "" ); // Improper expression if ( expr == old ) { if ( anyFound == null ) { throw " Syntax error, unrecognized expression: " + expr; } else { break ; } } old = expr; } return curLoop; };最关键是在加粗字代码,result.push( item ); anyFound = true; 和 curLoop = result;将匹配的元素加入result中,然后赋值于curLoop。
而方法的最后返回的是curLoop。所匹配的元素就是最后所需要的jQuery对象。
2. 【parent > child】
在给定的父元素下匹配所有的子元素。
HTML代码jQuery代码结果<form> <label>Name:</label> <input name="name" /> <fieldset> <label>Newsletter:</label> <input name="newsletter" /> </fieldset> </form> <input name="none" />$("form > input")[ <input name="name" /> ]
我们只要查看一下它的核心代码:
relative: { // " > " : function (checkSet, part, isXML){ // 当part为单词字符时,如$("form > input"),part为“form” if ( typeof part === " string " && ! / "W / .test(part) ) { part = isXML ? part : part.toUpperCase(); for ( var i = 0 , l = checkSet.length; i < l; i ++ ) { var elem = checkSet[i]; if ( elem ) { // 得到elem的父节点 var parent = elem.parentNode; // 如果父节点名称为part值时,在checkSet[i]上赋值父节点,否则赋值false checkSet[i] = parent.nodeName === part ? parent : false ; } } // 当part为非单词字符时,如$(".blue > input"),part为“.blue” } else { for ( var i = 0 , l = checkSet.length; i < l; i ++ ) { var elem = checkSet[i]; if ( elem ) { checkSet[i] = typeof part === " string " ? elem.parentNode : elem.parentNode === part; } } if ( typeof part === " string " ) { Sizzle.filter( part, checkSet, true ); } } }, }从这里我们可以得到checkSet的值集合。
2. 【prev + next】
匹配所有紧接在 prev 元素后的 next 元素。next (Selector) :一个有效选择器并且紧接着第一个选择器。
例子
HTML代码jQuery代码结果<form> <label>Name:</label> <input name="name" /> <fieldset> <label>Newsletter:</label> <input name="newsletter" /> </fieldset> </form> <input name="none" /> $("label + input")[ <input name="name" />, <input name="newsletter" /> ]
只要查看一下它的核心代码:
relative: { // " + " : function (checkSet, part){ for ( var i = 0 , l = checkSet.length; i < l; i ++ ) { var elem = checkSet[i]; if ( elem ) { // 得到elem的前一个节点 var cur = elem.previousSibling; // 当cur的节点类型不为元素节点的时候,继续得到cur的前一个节点,否则循环结束 while ( cur && cur.nodeType !== 1 ) { cur = cur.previousSibling; } checkSet[i] = typeof part === " string " ? cur || false : cur === part; } } if ( typeof part === " string " ) { Sizzle.filter( part, checkSet, true ); } }, // }从这里我们可以得到checkSet的值集合。
3. 【prev ~ next】
匹配 prev 元素之后的所有 siblings 元素。
例子
HTML代码jQuery代码结果<form> <label>Name:</label> <input name="name" /> <fieldset> <label>Newsletter:</label> <input name="newsletter" /> </fieldset> </form> <input name="none" /> <input name="none2" />$("form ~ input")[ <input name="none" />, <input name="none2" />]
只要查看一下它的核心代码:
relative: { // " ~ " : function (checkSet, part, isXML){ var doneName = " done " + (done ++ ), checkFn = dirCheck; if ( typeof part === " string " && ! part.match( / "W / ) ) { var nodeCheck = part = isXML ? part : part.toUpperCase(); checkFn = dirNodeCheck; } checkFn( " previousSibling " , part, doneName, checkSet, nodeCheck, isXML); } }其中checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); 调用的是dirCheck方法,它的具体实现为:
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { for ( var i = 0 , l = checkSet.length; i < l; i ++ ) { var elem = checkSet[i]; if ( elem ) { elem = elem[dir]; var match = false ; while ( elem && elem.nodeType ) { if ( elem[doneName] ) { match = checkSet[ elem[doneName] ]; break ; } if ( elem.nodeType === 1 ) { if ( ! isXML ) elem[doneName] = i; if ( typeof cur !== " string " ) { if ( elem === cur ) { match = true ; break ; } } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { match = elem; break ; } } // 由于这里dir为previousSibling,所以这里利用循环不断得到elem的前一个节点,并且赋值checkSet数组 elem = elem[dir]; } checkSet[i] = match; } } }从这里我们可以得到checkSet的值集合。
三、简单
1. 【:first】,【:last】,【:even】,【:odd】,【 :eq(index) 】,【 :gt(index) 】,【 :lt(index) 】和 【 :not(selector) 】
:first 匹配找到的第一个元素。
:last 匹配找到的最后一个元素。
:even 匹配所有索引值为偶数的元素,从 0 开始计数。
:odd 匹配所有索引值为奇数的元素,从 0 开始计数。
:eq(index) 匹配一个给定索引值的元素。
:gt(index) 匹配所有大于给定索引值的元素。
:lt(index) 匹配所有小于给定索引值的元素。
:not(selector) 去除所有与给定选择器匹配的元素。
例子
HTML代码jQuery代码<table> <tr><td>Header 1</td></tr> <tr><td>Value 1</td></tr> <tr><td>Value 2</td></tr> </table>$("tr:first"),$("tr:last"),$("tr:even"),$("tr:odd"),$("tr:eq(1)"),$("tr:gt(0)")
首先我们看下它的一个正则表达式:
POS: / :(nth|eq|gt|lt|first|last|even|odd)(?:"(("d*)"))?(?=[^-]|$) / ,核心代码从Sizzle.filter开始:
让我们先看下var filter = Expr.filter[ type ],的Expr.filter的具体实现,核心代码为:
接着看下Expr.setFilters的具体实现:
setFilters: { first: function (elem, i){ return i === 0 ; }, last: function (elem, i, match, array){ return i === array.length - 1 ; }, even: function (elem, i){ return i % 2 === 0 ; }, odd: function (elem, i){ return i % 2 === 1 ; }, lt: function (elem, i, match){ return i < match[ 3 ] - 0 ; }, gt: function (elem, i, match){ return i > match[ 3 ] - 0 ; }, nth: function (elem, i, match){ return match[ 3 ] - 0 == i; }, eq: function (elem, i, match){ return match[ 3 ] - 0 == i; } }噢,所有的标识 主要在这里判断elem元素在集合中的逻辑位置,并且返回一个布尔值。
接着 match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not ); Expr.prefilter的具体实现,主要核心代码为:
PSEUDO: function (match, curLoop, inplace, result, not){ if ( match[ 1 ] === " not " ) { // 代码1 if ( match[ 3 ].match(chunker).length > 1 ) { match[ 3 ] = Sizzle(match[ 3 ], null , null , curLoop); } else { var ret = Sizzle.filter(match[ 3 ], curLoop, inplace, true ^ not); if ( ! inplace ) { result.push.apply( result, ret ); } return false ; } } else if ( Expr.match.POS.test( match[ 0 ] ) ) { return true ; } return match; }当match[1]匹配中包含为“not”时,即表达式字符串中包含:not时,发生“代码1”;否则,根据POS的正则表达式判断返回true。
最后将匹配的item元素入栈,即 result.push( item )。Sizzle.filter最后返回的是curLoop。所匹配的元素就是最后所需要的jQuery对象。