Sizzle引擎--原生getElementsByClassName對選擇結果的影響(jQuery)


個人覺得這個例子雖然可能不具有實際意義,但是可以很好的理解Sizzle選擇的過程

實例說明

先看一個例子:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title></title>
</head>
<body>
<p class="tab" id="a1">1</p>
<p class="tab" id="a2">2</p>
<p class="tab" id="a3">3</p>
<script type="text/javascript" src="../sizzle.js"></script>
<script type="text/javascript">
console.log(Sizzle('.tab:not(:first)'));
console.log(Sizzle('p:not(:first)'));
console.log(Sizzle('p.tab:not(:first)'));
</script>
</body>
</html>

看上面三個結果的三個表達式,估計很多人會覺得結果肯定是一樣的,不錯,除去IE6/7/8,結果應該都是一樣的,結果(一):

    console.log(Sizzle('.tab:not(:first)'));  //[#a2,#a3]
console.log(Sizzle('p:not(:first)')); //[#a2,#a3]
console.log(Sizzle('p.tab:not(:first)')); //[#a2,#a3]

但是在IE6/7/8下面,結果(二):

    console.log(Sizzle('.tab:not(:first)'));  //[#a1,#a2,#a3]
console.log(Sizzle('p:not(:first)')); //[#a2,#a3]
console.log(Sizzle('p.tab:not(:first)')); //[#a2,#a3]

其實不僅是IE6/7/8,任何不支持getElementsByClassName方法的瀏覽器結果都是結果(二)這樣。

結果分析

在結果(一)的過程中,

'.tab:not(:first)'選擇流程是:
1、document.getElementsByClassName('.tab')獲得結果集[#a1,#a2,#a3];
2、過濾:not(:first),獲得結果集[#a2,#a3];
'p:not(:first)'選擇流程是:
1、document.getElementsByTagName('p')獲得結果集[#a1,#a2,#a3];
2、過濾:not(:first),獲得結果集[#a2,#a3];
'p.tab:not(:first)'選擇流程是:
1、document.getElementsByClassName('.tab')獲得結果集[#a1,#a2,#a3];
2、過濾p,獲得結果集[#a1,#a2,#a3];
3、過濾:not(:first),獲得結果集[#a2,#a3];

在結果(二)的過程中:

'.tab:not(:first)'選擇流程是:
1、document.getElementsByTagName('*')獲得結果集[html,head,body,#a1,#a2,#a3,script,script]
2、過濾:not(:first),獲得結果集[head,body,#a1,#a2,#a3,script,script]
3、過濾.tab,獲得結果集[#a1,#a2,#a3]
'p:not(:first)'選擇流程是:
1、document.getElementsByTagName('p')獲得結果集[#a1,#a2,#a3];
2、過濾:not(:first),獲得結果集[#a2,#a3];
'p.tab:not(:first)'選擇流程是:
1、document.getElementsByTagName('p')獲得結果集[#a1,#a2,#a3];
2、過濾:not(:first),獲得結果集[#a2,#a3];
3、過濾.tab,獲得結果集[#a2,#a3]

可以看到,在不含class的選擇符中,兩種情況過程是一樣的,當含有class的時候,會優先去尋找含有class的元素,從而直接影響了后面過濾步驟的候選集。

原因分析

至於產生的原因,我們回到代碼層面來解釋:

第一個影響因素:

先看Sizzle.find部分

for ( i = 0, len = Expr.order.length; i < len; i++ ) {}

Expr.order: [ "ID", "NAME", "TAG" ];


最初的查找的時候,匹配type的是有一個優先級的ID-->NAME-->TAG,對於支持getElementsByClassName的瀏覽器,Sizzle源碼進行了一個處理:

在1296行:

Expr.order.splice(1, 0, "CLASS");
Expr.find.CLASS = function( match, context, isXML ) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};

於是在支持getElementsByClassName的瀏覽器中,Sizzle.find的實現變成

for ( i = 0, len = Expr.order.length; i < len; i++ ) {}

Expr.order: [ "ID","CLASS" "NAME", "TAG" ];

而且CLASS的優先級僅次於ID。

所以.tab:not(:first)在過濾的第一步,不同瀏覽器的候選集就已經發生了差異。

 

第二個影響因素:

再看Sizzle.filter部分

for ( type in Expr.filter ) {}
Expr.filter={
CLASS: function( match, curLoop, inplace, result, not, isXML ) {},
ID: function( match ) {},
TAG: function( match, curLoop ) {},
CHILD: function( match ) {},
ATTR: function( match, curLoop, inplace, result, not, isXML ) {},
PSEUDO: function( match, curLoop, inplace, result, not ) {}
POS: function( match ) {}
},


這里與Sizzle.find的一個顯著不同是這里用的for..in循環,沒有確定的順序【優先級】,因此並不能保證先過濾什么類型。

所以.tab:not(:first)在先過濾了:not(:first),然后再過濾.tab,所以結果和想想的不一致。

第三個影響因素:

這個涉及到filter的實現,對於含有not的表達式,候選集合會被篩選一遍。

.tab:not(:first)在not部分集合改變,直接去除了第一個元素。

.tab:first 就沒有改變,因為first是在POS匹配中處理的。

 

解決方案

如果我們把Sizzle.filter的實現改成和Sizzle.find一致,變成如下形式:

for ( i = 0, len = Expr.filterOrder.length; i < len; i++ ) {}

Expr.filterOrder: [ "ID","CLASS" "NAME", "TAG" ,"ATTR","CHILD","PSEUDO","POS"];


那么我們就可以保證選擇的結果和預期的一致。不過這種牽一發而動全身的事情,顯然是不適合的。

另外一個方法就是避免使用這種有歧義的選擇符,將:not(:first)作為集合的一個方法調用,而不直接寫到選擇表達式里面。

 另外,如果知道DOM的結構,處理方式就很多了,比如加tag限制,層級限制等等

說明

這種問題並不是出現在沒個類似的選擇表達式中,具體的情況可以查看Sizzle源碼的filter部分。

filter部分比較煩,而且最近沒時間寫filter部分的東西,有什么錯誤之處,歡迎交流。

轉載請注明來自小西山子【http://www.cnblogs.com/xesam/
本文地址:http://www.cnblogs.com/xesam/archive/2012/02/21/2361591.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM