如何理解與簡化jQuery的closest函數(shù)
在實現(xiàn)delegate方法中,有一個很重要的輔助函數(shù)叫closest,雖然現(xiàn)在它歸類為遍歷節(jié)點這個模塊中。這個函數(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對象與使用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 );
- },
恰逢我也想造個輪子,便去研究它一翻,發(fā)現(xiàn)其***個可以是字符串,元素節(jié)點或jQuery對象,還有一個可選參數(shù),上下文??丛创a前幾句,發(fā)現(xiàn)有個分支是判斷是否是Array,估計是供內(nèi)部調(diào)用的優(yōu)化代碼,可以無視之。于是其方法簡化為:
- closest: function( selectors, context ) {
- var ret = [], i, l, cur = this[0];
- // 如果字符串包含位置偽類或者是個元素節(jié)點,則封裝為一個jQuery對象,否則為0(即false的簡寫,用于快速跳過分支)
- var pos = POS.test( selectors ) || typeof selectors !== "string" ?
- jQuery( selectors, context || this.context ) :
- 0;
- //遍歷原jQuery對象的節(jié)點
- for ( i = 0, l = this.length; i < l; i++ ) {
- cur = this[i];
- while ( cur ) {
- //如果是jQuery對象,則判定其是否包含當前節(jié)點,否則使用matchesSelector方法判定這個節(jié)點是否匹配給定的表達式selectors
- if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- //是則放入選擇器中
- ret.push( cur );
- break;
- } else {
- // 否則把當前節(jié)點變?yōu)槠涓腹?jié)點
- cur = cur.parentNode;
- if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
- break;
- }
- }
- }
- }
- //如果大于1,進行唯一化操作
- ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
- //將節(jié)點集合重新包裝成一個新jQuery對象返回
- return this.pushStack( ret, "closest", selectors );
- },
由于本人很反感位置偽類,認為其違反選擇器的法則之一(由關(guān)系選擇器隔開的各選擇器分組內(nèi)部,它們的位置是隨意的),因此有關(guān)位置偽類的邏輯我也去掉了。
- closest: function( selectors ) {
- var ret = [], i, l, cur = this[0];
- // 如果字符串包含位置偽類或者是個元素節(jié)點,則封裝為一個jQuery對象,否則為0(即false的簡寫,用于快速跳過分支)
- var node = selectors.nodeType ? selectors :false;
- var nodes = [].slice.call(this);//將jQuery對象轉(zhuǎn)換為純數(shù)組
- //遍歷原jQuery對象的節(jié)點
- for ( i = 0, l = this.length; i < l; i++ ) {
- cur = this[i];
- while ( cur ) {
- //如果是jQuery對象,則判定其是否包含當前節(jié)點,否則使用matchesSelector方法判定這個節(jié)點是否匹配給定的表達式selectors
- if ( obj ? nodes.indexOf(node) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
- //indexOf方法在某些瀏覽器需要自行實現(xiàn)
- //是則放入選擇器中
- ret.push( cur );
- break;
- } else {
- // 否則把當前節(jié)點變?yōu)槠涓腹?jié)點
- cur = cur.parentNode;
- //如果沒有父節(jié)點(說明其還沒有插入DOM樹),或不是元素節(jié)點,或是文檔碎片(說明其剛被移出DOM樹)
- if ( !cur || !cur.ownerDocument || cur.nodeType === 11 ) {
- break;
- }
- }
- }
- }
- //如果大于1,進行唯一化操作
- ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
- //將節(jié)點集合重新包裝成一個新jQuery對象返回
- return $(ret);//本人覺得pushStack真是個邪惡的方法,讓菜鳥不籽有鏈下去的欲望,殊不知這是維護的大敵
- },
注意,jquery1.6中closest方法不再是返回包含一個或零個節(jié)點的jQuery對象了,再是對應(yīng)多個了(因此jQuery官網(wǎng)文檔是錯誤的,沒有即時同步這變化.)
- <!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>
下面是我的實現(xiàn):
- closest: function( expr ) {
- // 如果字符串包含位置偽類或者是個元素節(jié)點,則封裝為一個dom對象,否則為0(即false的簡寫,用于快速跳過分支)
- var node = expr.nodeType ? expr : 0, nodes = dom.slice(this);//將它轉(zhuǎn)換為純數(shù)組
- //遍歷原dom對象的節(jié)點
- for (var i = 0, ret = [], cur; cur = this[i++];) {//由于肯定里面都是節(jié)點,因此可以使用這種循環(huán)
- while (cur && cur.nodeType === 1 ) {
- //如果是dom對象,則判定其是否包含當前節(jié)點,否則使用matchesSelector方法判定這個節(jié)點是否匹配給定的表達式expr
- if ( node ? nodes.indexOf(node) > -1 : matchElement( cur, expr ) ){
- //indexOf方法在某些瀏覽器需要自行實現(xiàn)
- //是則放入選擇器中
- ret.push( cur );
- break;
- } else {
- // 否則把當前節(jié)點變?yōu)槠涓腹?jié)點
- cur = cur.parentNode;
- }
- }
- }
- //如果大于1,進行唯一化操作
- ret = ret.length > 1 ? dom.unique( ret ) : ret;
- //將節(jié)點集合重新包裝成一個新dom對象返回
- return this.labor(ret);
- },
原文鏈接:http://www.cnblogs.com/rubylouvre/archive/2011/05/12/2043854.html
【編輯推薦】
- 詳解jQuery構(gòu)造器的實現(xiàn)
- jQuery調(diào)用WCF開發(fā)實例經(jīng)驗分享
- 5月***超有趣的免費jQuery插件推薦
- 手把手教你使用jQuery操作元素的屬性與樣式
- jQuery性能指標和調(diào)優(yōu)