在實(shí)現(xiàn)delegate方法中,有一個(gè)很重要的輔助函數(shù)叫closest,雖然現(xiàn)在它歸類為遍歷節(jié)點(diǎn)這個(gè)模塊中。這個(gè)函數(shù)實(shí)現(xiàn)得非常復(fù)雜,洋洋灑灑近50行,完全不符合極限編程的規(guī)矩。
closest: function( selectors, context ) {
var ret = [], i, l, cur = this[0];
// Array
if ( jQuery.isArray( selectors ) ) {//這分支的過濾邏輯基本與下面的一致
var match, selector,
matches = {},
level = 1;
if ( cur && selectors.length ) {
for ( i = 0, l = selectors.length; i < l; i++ ) {
selector = selectors[i];
if ( !matches[ selector ] ) {
matches[ selector ] = POS.test( selector ) ?
jQuery( selector, context || this.context ) :
selector;
}
}
while ( cur && cur.ownerDocument && cur !== context ) {
for ( selector in matches ) {
match = matches[ selector ];//這里頻繁創(chuàng)建新的jQuery對(duì)象與使用is這樣復(fù)雜的方法,我不覺得其高效到哪里去
if ( match.jquery ? match.index( cur ) > -1 : jQuery( cur ).is( match ) ) {
ret.push({ selector: selector, elem: cur, level: level });
}
}
cur = cur.parentNode;
level++;
}
}
return ret;
}
// String
var pos = POS.test( selectors ) || typeof selectors !== "string" ?
jQuery( selectors, context || this.context ) :
0;
for ( i = 0, l = this.length; i < l; i++ ) {
cur = this[i];
while ( cur ) {
if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
ret.push( cur );
break;
} else {
cur = cur.parentNode;
if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
break;
}
}
}
}
ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
return this.pushStack( ret, "closest", selectors );
},
恰逢我也想造個(gè)輪子,便去研究它一翻,發(fā)現(xiàn)其第一個(gè)可以是字符串,元素節(jié)點(diǎn)或jQuery對(duì)象,還有一個(gè)可選參數(shù),上下文。看源碼前幾句,發(fā)現(xiàn)有個(gè)分支是判斷是否是Array,估計(jì)是供內(nèi)部調(diào)用的優(yōu)化代碼,可以無(wú)視之。于是其方法簡(jiǎn)化為:
closest: function( selectors, context ) {
var ret = [], i, l, cur = this[0];
// 如果字符串包含位置偽類或者是個(gè)元素節(jié)點(diǎn),則封裝為一個(gè)jQuery對(duì)象,否則為0(即false的簡(jiǎn)寫,用于快速跳過分支)
var pos = POS.test( selectors ) || typeof selectors !== "string" ?
jQuery( selectors, context || this.context ) :
0;
//遍歷原jQuery對(duì)象的節(jié)點(diǎn)
for ( i = 0, l = this.length; i < l; i++ ) {
cur = this[i];
while ( cur ) {
//如果是jQuery對(duì)象,則判定其是否包含當(dāng)前節(jié)點(diǎn),否則使用matchesSelector方法判定這個(gè)節(jié)點(diǎn)是否匹配給定的表達(dá)式selectors
if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
//是則放入選擇器中
ret.push( cur );
break;
} else {
// 否則把當(dāng)前節(jié)點(diǎn)變?yōu)槠涓腹?jié)點(diǎn)
cur = cur.parentNode;
if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
break;
}
}
}
}
//如果大于1,進(jìn)行唯一化操作
ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
//將節(jié)點(diǎn)集合重新包裝成一個(gè)新jQuery對(duì)象返回
return this.pushStack( ret, "closest", selectors );
},
由于本人很反感位置偽類,認(rèn)為其違反選擇器的法則之一(由關(guān)系選擇器隔開的各選擇器分組內(nèi)部,它們的位置是隨意的),因此有關(guān)位置偽類的邏輯我也去掉了。
closest: function( selectors ) {
var ret = [], i, l, cur = this[0];
// 如果字符串包含位置偽類或者是個(gè)元素節(jié)點(diǎn),則封裝為一個(gè)jQuery對(duì)象,否則為0(即false的簡(jiǎn)寫,用于快速跳過分支)
var node = selectors.nodeType ? selectors :false;
var nodes = [].slice.call(this);//將jQuery對(duì)象轉(zhuǎn)換為純數(shù)組
//遍歷原jQuery對(duì)象的節(jié)點(diǎn)
for ( i = 0, l = this.length; i < l; i++ ) {
cur = this[i];
while ( cur ) {
//如果是jQuery對(duì)象,則判定其是否包含當(dāng)前節(jié)點(diǎn),否則使用matchesSelector方法判定這個(gè)節(jié)點(diǎn)是否匹配給定的表達(dá)式selectors
if ( obj ? nodes.indexOf(node) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
//indexOf方法在某些瀏覽器需要自行實(shí)現(xiàn)
//是則放入選擇器中
ret.push( cur );
break;
} else {
// 否則把當(dāng)前節(jié)點(diǎn)變?yōu)槠涓腹?jié)點(diǎn)
cur = cur.parentNode;
//如果沒有父節(jié)點(diǎn)(說(shuō)明其還沒有插入DOM樹),或不是元素節(jié)點(diǎn),或是文檔碎片(說(shuō)明其剛被移出DOM樹)
if ( !cur || !cur.ownerDocument || cur.nodeType === 11 ) {
break;
}
}
}
}
//如果大于1,進(jìn)行唯一化操作
ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
//將節(jié)點(diǎn)集合重新包裝成一個(gè)新jQuery對(duì)象返回
return $(ret);//本人覺得pushStack真是個(gè)邪惡的方法,讓菜鳥不籽有鏈下去的欲望,殊不知這是維護(hù)的大敵
},
注意,jquery1.6中closest方法不再是返回包含一個(gè)或零個(gè)節(jié)點(diǎn)的jQuery對(duì)象了,再是對(duì)應(yīng)多個(gè)了(因此jQuery官網(wǎng)文檔是錯(cuò)誤的,沒有即時(shí)同步這變化.)
<!doctype html>
<html>
<head>
<title>closest在jquery1.6的改變 by 司徒正美</title>
<script src="jquery.js"></script>
<script>
$(function(){
$("p").delegate("strong","click",function(){
alert(this.innerHTML)
});
alert($("strong").closest("p").length)
});
</script>
</head>
<body>
<p>
<strong>使用事件代理1</strong>
</p>
<p>
<strong>使用事件代理2</strong>
</p>
<p>
<strong>使用事件代理3</strong>
</p>
</body>
</html>
下面是我的實(shí)現(xiàn):
closest: function( exPR ) {
// 如果字符串包含位置偽類或者是個(gè)元素節(jié)點(diǎn),則封裝為一個(gè)dom對(duì)象,否則為0(即false的簡(jiǎn)寫,用于快速跳過分支)
var node = expr.nodeType ? expr : 0, nodes = dom.slice(this);//將它轉(zhuǎn)換為純數(shù)組
//遍歷原dom對(duì)象的節(jié)點(diǎn)
for (var i = 0, ret = [], cur; cur = this[i++];) {//由于肯定里面都是節(jié)點(diǎn),因此可以使用這種循環(huán)
while (cur && cur.nodeType === 1 ) {
//如果是dom對(duì)象,則判定其是否包含當(dāng)前節(jié)點(diǎn),否則使用matchesSelector方法判定這個(gè)節(jié)點(diǎn)是否匹配給定的表達(dá)式expr
if ( node ? nodes.indexOf(node) > -1 : matchElement( cur, expr ) ){
//indexOf方法在某些瀏覽器需要自行實(shí)現(xiàn)
//是則放入選擇器中
ret.push( cur );
break;
} else {
// 否則把當(dāng)前節(jié)點(diǎn)變?yōu)槠涓腹?jié)點(diǎn)
cur = cur.parentNode;
}
}
}
//如果大于1,進(jìn)行唯一化操作
ret = ret.length > 1 ? dom.unique( ret ) : ret;
//將節(jié)點(diǎn)集合重新包裝成一個(gè)新dom對(duì)象返回
return this.labor(ret);
},
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注