还漏了一个框题,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对象。