jQuery 源碼分析(十九) DOM遍歷模塊詳解

来源:https://www.cnblogs.com/greatdesert/archive/2019/11/04/11724756.html
-Advertisement-
Play Games

jQuery的DOM遍歷模塊對DOM模型的原生屬性parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling進行了封裝和擴展,用於在DOM樹中遍歷父元素、子元素和兄弟元素。 可以通過jQuery的實例來訪問,方法如下: ...


jQuery的DOM遍歷模塊對DOM模型的原生屬性parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling進行了封裝和擴展,用於在DOM樹中遍歷父元素、子元素和兄弟元素。

可以通過jQuery的實例來訪問,方法如下:

  •   parent()                  ;獲取匹配元素的父元素                
  •   parents(selector)           ;獲取匹配元素的所有祖先元素                                    ;selector用於過濾查找的元素,如果為空則獲取所有的祖先元素            
  •   parentsUntil(until,selector)    ;獲得當前匹配元素集合中每個元素的祖先元素,直到遇到until元素        
  •   next()                     ;獲取匹配元素之後緊挨著的兄弟元素,沒有則返回undefined
  •   nextAll(selector)               ;獲取匹配元素之後緊挨著的所有兄弟元素             
  •   nextUntil(until,selector)       ;獲取匹配元素之後緊挨著的所有兄弟元素,直到遇到until元素 
  •   prev()                    ;獲取匹配元素之前緊挨著的兄弟元素,沒有則返回undefined
  •   prevAll(selector)              ;獲取匹配元素之前緊挨著的所有兄弟元素             
  •   prevUntil(until,selector)        ;獲取匹配元素之前緊挨著的所有兄弟元素,直到遇到until元素     
  •   siblings(selector)            ;獲取匹配元素的所有兄弟元素,如果指定了selector則只獲取該類型的元素 
  •   children(selector)           ;獲取匹配元素的子元素,不包括文本節點和註釋節點         
  •   contents()                  ;獲取匹配元素的子元素,包括文本節點和註釋節點 

舉個慄子:

 writer by:大沙漠 QQ:22969969

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <h1>Hello World!</h1>
        <p>123</p>
        <span>123</span>        
    </div>
    <script>
        console.log( $('p').parent() )            //獲取p元素的父元素
        console.log( $('p').prev() )              //獲取p元素之前緊挨的兄弟元素
        console.log( $('p').next() )              //獲取p元素之後緊挨的兄弟元素
        console.log( $('p').siblings() )          //獲取p元素的所有兄弟元素
        console.log( $('div').children() )        //獲取div的所有子元素,不包括文本節點和註釋節點
        console.log( $('div').contents() )        //獲取div的所有子元素,包括文本節點和註釋節點
    </script>
</body>
</html>

輸出如下:

 對應慄子里的每一個輸出,分別輸出

  • p元素的父節點
  • p元素的上一個兄弟元素
  • p元素的下一個兄弟元素
  • p元素的所有兄弟元素
  • div元素過濾掉文本節點和空白節點的子節點
  • div元素包括文本節點和空白節點的子節點

返回的都是一個jQuery對象,非常的好用,操作DOM時都會用到這幾個方法

 

源碼分析


 jQuery對於DOM遍歷模塊的實現是基於三個工具函數實現的

  • $.dir(elem, dir, until)       ;從elem元素開始,查找dir方向(可以是:parentNode、nextSibling或previousSibling)上的所有元素,如果在查找過程中遇到匹配參數until的元素,則查找終止
  • $.nth(cur, result, dir, elem)  ;從cur元素出發,查找dir方向(parentNode、nextSibling或previousSibling)上的第result個元素
  • $.sibling(n, elem)       ;負責查找一個元素之後的所有兄弟元素,包括起始元素,但不包括參數elem。n是查找的起始元素,包含在結果集中,elem是可選的DOM元素,不包含在返回結果中。

舉個慄子:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="http://libs.baidu.com/jquery/1.7.1/jquery.min.js"></script>
</head>
<body>
    <div>
        <h1>Hello World!</h1>
        <p>123</p>
        <span>123</span>        
    </div>
    <script>    
        var h1 = document.getElementsByTagName('h1')[0],          //分別獲取h1、p、span元素的節點引用
            p = document.getElementsByTagName('p')[0],
            span = document.getElementsByTagName('span')[0];
        console.log( $.dir(h1,'parentNode') )                     //列印h1的所有祖先節點            
        console.log( $.nth(h1,3,'nextSibling') )                  //獲取h1之後的第二個節點
        console.log( $.sibling(h1,h1) )                           //獲取h1的所有兄弟元素,預設獲取包括自身在內的元素,參數2也傳入h1表示排除h1元素
    </script>
</body>
</html>

輸出如下:

輸出三行,分別對應h1的所有祖先節點、h1之後的第二個子節點和h1的所有兄弟元素,和DOM樹中也是對應的。

對於$.dir、$.nth和$.sibling來說,它的實現如下:

jQuery.extend({
    dir: function( elem, dir, until ) {        //從elem元素開始,查找dir方向(可以是:parentNode、nextSibling或previousSibling)上的所有元素,如果在查找過程中遇到匹配參數until的元素,則查找終止。
        var matched = [],
            cur = elem[ dir ];                    //cur表示下一個元素,根據參數dir不同,可能是父元素、前一個兄弟元素 或者 後一個兄弟元素

        while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {    //如果下一個元素存在 且 不是document對象(nodeType等於9) 則繼續判斷括弧里的語句,否則跳出迴圈  括弧內:如果沒有傳入until參數,或者傳入了until參數並且當前元素不是Element節點(屬性nodeType等於1)    或者 傳入了until參數且當前參數是Element節點且該元素不匹配參數until 則進行迴圈
            if ( cur.nodeType === 1 ) {                    //如果cur是元素節點
                matched.push( cur );                        //存儲到數組matched中
            }
            cur = cur[dir];                                //獲取下一個方向上的元素
        }
        return matched;                                //返回找到了的Element節點數組。
    },

    nth: function( cur, result, dir, elem ) {    //從cur元素出發,查找dir方向(parentNode、nextSibling或previousSibling)上的第result個元素
        result = result || 1;
        var num = 0;                                //當前查找的元素序號

        for ( ; cur; cur = cur[dir] ) {                    //從起始元素cur觸發,沿著方向dir迭代遍歷,只要cur元素存在則一直查找,直到查找該方向上的第result個Element節點
            if ( cur.nodeType === 1 && ++num === result ) {    //如果cur是一個元素節點,則num+1,此時如果num等於result,則表示查找到該元素了,則跳出迴圈
                break;
            }
        }

        return cur;                                        //返回查找到的cur元素。
    },

    sibling: function( n, elem ) {                //負責查找一個元素之後的所有兄弟元素,包括起始元素,但不包括參數elem。n是查找的起始元素,包含在結果集中,elem是可選的DOM元素,不包含在返回結果中。
        var r = [];

        for ( ; n; n = n.nextSibling ) {                //從起始元素n觸發,迭代遍歷其後的所有兄弟元素
            if ( n.nodeType === 1 && n !== elem ) {            //如果該兄弟元素是元素節點且不等於參數elem則被放入數組r中。
                r.push( n );
            }
        }

        return r;                                        //返回存放了找到的Element節點的數組r
    }
});

也就是對parentNode、childNodes、firstChild、lastChild、previousSibling、nextSibling這些原生DOM操作的一些封裝

我們通過jQuery實例可以訪問的方法的實現如下:

jQuery.each({
    parent: function( elem ) {                        //返回匹配元素的父元素
        var parent = elem.parentNode;
        return parent && parent.nodeType !== 11 ? parent : null;    //如果父元素存在且不是文檔碎片元素,則返回該父元素,否則返回null
    },
    parents: function( elem ) {                        //返回匹配元素的所有祖先元素
        return jQuery.dir( elem, "parentNode" );
    },
    parentsUntil: function( elem, i, until ) {        //獲得當前匹配元素集合中每個元素的祖先元素,直到遇到until元素
        return jQuery.dir( elem, "parentNode", until );
    },
    next: function( elem ) {                        //獲取匹配元素之後緊挨著的兄弟元素    
        return jQuery.nth( elem, 2, "nextSibling" );
    },
    prev: function( elem ) {                        //獲取匹配元素之前緊挨著的兄弟元素
        return jQuery.nth( elem, 2, "previousSibling" );
    },
    nextAll: function( elem ) {                        //獲取匹配元素之後緊挨著的所有兄弟元素    
        return jQuery.dir( elem, "nextSibling" );
    },
    prevAll: function( elem ) {                        //獲取匹配元素之前緊挨著的所有兄弟元素
        return jQuery.dir( elem, "previousSibling" );
    },
    nextUntil: function( elem, i, until ) {            //獲取匹配元素之後緊挨著的所有兄弟元素    ,直到遇到匹配元素until為止
        return jQuery.dir( elem, "nextSibling", until );
    },
    prevUntil: function( elem, i, until ) {            //獲取匹配元素之前緊挨著的所有兄弟元素    ,直到遇到匹配元素until為止
        return jQuery.dir( elem, "previousSibling", until );
    },
    siblings: function( elem ) {                    //獲取匹配元素的所有兄弟元素
        return jQuery.sibling( elem.parentNode.firstChild, elem );
    },
    children: function( elem ) {                    //獲取匹配元素的子元素
        return jQuery.sibling( elem.firstChild );
    },
    contents: function( elem ) {                    //獲取匹配元素的子元素,包括文本節點和註釋節點
        return jQuery.nodeName( elem, "iframe" ) ?
            elem.contentDocument || elem.contentWindow.document :
            jQuery.makeArray( elem.childNodes );
    }
}, function( name, fn ) {                            //模板函數,用於調用對應的遍歷函數查找DOM元素,然後執行過濾、排序、和去重操作         name是每個函數名,fn是對應的值(也就是函數)
    jQuery.fn[ name ] = function( until, selector ) {        //定義模版函數,真正是在這裡定義的,它接收兩個參數 until:選擇器表達式,用於指示查找停止的位置。selector:選擇器表達式,用於過濾找到的元素
        var ret = jQuery.map( this, fn, until );                //調用方法jQuery.map()遍歷當前匹配元素集合,對每個元素調用遍歷函數fn,並將遍歷函數fn的返回值放入一個新的數組ret中。

        if ( !runtil.test( name ) ) {                        //如果遍歷函數名不以'Until'結尾的,則最多只有一個參數
            selector = until;                                    //修正參數selector為until
        }

        if ( selector && typeof selector === "string" ) {    //如果設置了參數selector 且selector是一個字元串
            ret = jQuery.filter( selector, ret );                //則調用jQuery.filter()對數組ret中的DOM元素逐個過濾,最終只保留匹配選擇器表達式selector的元素。
        }

        ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;

        if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {        //排序
            ret = ret.reverse();                
        }

        return this.pushStack( ret, name, slice.call( arguments ).join(",") );    //創建一個jQuery對象並返回
    };
});

實現就是通過$.each()遍歷一個對象,依次執行參數2這個函數,該函數會在$.fn上依次掛載每個介面,使用$.map()工具方法它會依次執行fn函數,進行一些過濾去重後最後調用$.pushStack()將數組轉換為一個jQuery對象,這裡理解起來有點難度,需要多理一下代碼。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 購物車頁面的設計圖 商品的列表 代碼: 1 <ul v-if="shoppingListData.rows.length"> 2 <li 3 v-for="(item,index) in shoppingListData.rows" 4 :key="index" 5 > 6 <van-checkbo ...
  • 1、頁面如圖所示 2、Html代碼 3、JavaScript代碼 4、細節 (圖片來源於網路) 5、精確定位 HTML精確定位:scrollLeft,scrollWidth,clientWidth,offsetWidth scrollHeight: 獲取對象的滾動高度。 scrollLeft:設置或 ...
  • 瀏覽器緩存的解決方案 ——IT唐伯虎 摘要:瀏覽器緩存的解決方案,包括傳統前端和現代前端。 前言:本文只針對文件請求(html、css、js)進行分析,但不涉及json數據請求。 瀏覽器的狀態 (1)當瀏覽器向伺服器發起請求,如果請求正常,狀態是200。 (2)瀏覽器接收到請求結果後,如果會根據響應 ...
  • 我們只需要一段載入代碼就可以搞定MarkDown載入模板文件。 相關推薦: 1.在Asp.Net或.Net Core中配置使用MarkDown富文本編輯器有開源模板代碼(代碼是.net core3.0版本) 2.在Asp.Net Core中配置使用MarkDown富文本編輯器實現圖片上傳和截圖上傳( ...
  • a.小程式不支持<embed>標簽。 1. createPreviewVideo 所在位置video.js 代碼: $("#eduiVideoPreview", me.$widget)[0].innerHTML = '<video pluginspage="http://www.macromedia ...
  • Array.prototype.concat.call(array1, array2, array3, ...) ...
  • <template> <div :class="className"> <div :id="id" class="spiritChartBox"></div> </div> </template> <script> import { mapState } from "vuex"; import ec... ...
  • JS使用方法 模態窗提供了四個事件: 1.show.bs.modal在顯示之前觸發 2.shown.bs.modal在顯示之後觸發 3.hide.bs.modal在隱藏之前觸發 4.hidden.bs.modal在隱藏之後觸發 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...