如何理解與簡化jquery的closest函數

來源:互聯網
上載者:User

在實現delegate方法中,有一個很重要的輔助函數叫closest,雖然現在它歸類為遍曆節點這個模組中。這個函數實現得非常複雜,洋洋洒洒近50行,完全不符合極限編程的規矩。

closest: function( selectors, context ) {var ret = [], i, l, cur = this[0];// Arrayif ( jQuery.isArray( selectors ) ) {//這分支的過濾邏輯基本與下面的一致var match, selector,matches = {},level = 1;if ( cur && selectors.length ) {for ( i = 0, l = selectors.length; i  -1 : jQuery( cur ).is( match ) ) {ret.push({ selector: selector, elem: cur, level: level });}}cur = cur.parentNode;level++;}}return ret;}// Stringvar pos = POS.test( selectors ) || typeof selectors !== "string" ?jQuery( selectors, context || this.context ) :0;for ( i = 0, l = this.length; i  -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 );},

恰逢我也想造個輪子,便去研究它一翻,發現其第一個可以是字串,元素節點或jQuery對象,還有一個選擇性參數,上下文。看源碼前幾句,發現有個分支是判斷是否是Array,估計是供內部調用的最佳化代碼,可以無視之。於是其方法簡化為:

closest: function( selectors, context ) {    var ret = [], i, l, cur = this[0];       // 如果字串包含位置偽類或者是個元素節點,則封裝為一個jQuery對象,否則為0(即false的簡寫,用於快速跳過分支)    var pos = POS.test( selectors ) || typeof selectors !== "string" ?        jQuery( selectors, context || this.context ) :        0;    //遍曆原jQuery對象的節點    for ( i = 0, l = this.length; i  -1 : jQuery.find.matchesSelector(cur, selectors) ) {                //是則放入選取器中                ret.push( cur );                break;            } else {                //  否則把當前節點變為其父節點                cur = cur.parentNode;                if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {                    break;                }            }        }    }    //如果大於1,進行唯一化操作    ret = ret.length > 1 ? jQuery.unique( ret ) : ret;    //將節點集合重新封裝成一個新jQuery對象返回    return this.pushStack( ret, "closest", selectors );},

由於本人很反感位置偽類,認為其違反選取器的法則之一(由關係選取器隔開的各選取器分組內部,它們的位置是隨意的),因此有關位置偽類的邏輯我也去掉了。

closest: function( selectors ) {    var ret = [], i, l, cur = this[0];    // 如果字串包含位置偽類或者是個元素節點,則封裝為一個jQuery對象,否則為0(即false的簡寫,用於快速跳過分支)    var node =  selectors.nodeType ? selectors :false;    var nodes = [].slice.call(this);//將jQuery對象轉換為純數組    //遍曆原jQuery對象的節點    for ( i = 0, l = this.length; i  -1 : jQuery.find.matchesSelector(cur, selectors) ) {                //indexOf方法在某些瀏覽器需要自行實現                //是則放入選取器中                ret.push( cur );                break;            } else {                //  否則把當前節點變為其父節點                cur = cur.parentNode;                //如果沒有父節點(說明其還沒有插入DOM樹),或不是元素節點,或是文檔片段(說明其剛被移出DOM樹)                if ( !cur || !cur.ownerDocument || cur.nodeType === 11 ) {                    break;                }            }        }    }    //如果大於1,進行唯一化操作    ret = ret.length > 1 ? jQuery.unique( ret ) : ret;    //將節點集合重新封裝成一個新jQuery對象返回    return $(ret);//本人覺得pushStack真是個邪惡的方法,讓菜鳥不籽有鏈下去的慾望,殊不知這是維護的大敵},

注意,jquery1.6中closest方法不再是返回包含一個或零個節點的jQuery對象了,再是對應多個了(因此jQuery官網文檔是錯誤的,沒有即時同步這變化.)

<!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>

下面是我的實現:

            closest: function( expr ) {                // 如果字串包含位置偽類或者是個元素節點,則封裝為一個dom對象,否則為0(即false的簡寫,用於快速跳過分支)                var node =  expr.nodeType ? expr : 0, nodes = dom.slice(this);//將它轉換為純數組                //遍曆原dom對象的節點                for (var i = 0, ret = [], cur; cur = this[i++];) {//由於肯定裡面都是節點,因此可以使用這種迴圈                    while (cur && cur.nodeType === 1 ) {                        //如果是dom對象,則判定其是否包含當前節點,否則使用matchesSelector方法判定這個節點是否匹配給定的運算式expr                        if ( node ? nodes.indexOf(node) > -1 : matchElement( cur, expr ) ){                            //indexOf方法在某些瀏覽器需要自行實現                            //是則放入選取器中                            ret.push( cur );                            break;                        } else {                            // 否則把當前節點變為其父節點                            cur = cur.parentNode;                        }                    }                }                //如果大於1,進行唯一化操作                ret = ret.length > 1 ? dom.unique( ret ) : ret;                //將節點集合重新封裝成一個新dom對象返回                return this.labor(ret);            },

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.