<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      學(xué)習(xí) TreeWalker api 并與普通遍歷 DOM 方式進行比較

      介紹 TreeWalker

      TreeWalkerJavaScript 中用于遍歷 DOM 樹的一個接口。允許你以靈活的方式在 DOM 樹中進行前向和后向遍歷,包括訪問父節(jié)點、子節(jié)點和兄弟節(jié)點。適用于處理復(fù)雜的 DOM 操作:在遍歷過程中進行添加、刪除或修改節(jié)點的操作,并繼續(xù)遍歷。

      與普通的 for 循環(huán) + querySelector 相比靈活性更高。執(zhí)行速度方面,在 DOM 超過一定復(fù)雜度的情況下,TreeWalker 更快,后面會舉例說明。

      實踐

      創(chuàng)建 TreeWalker

      可以使用 document.createTreeWalker 方法來創(chuàng)建一個 TreeWalker 對象。這個方法接受四個參數(shù):

      • root:要遍歷的根節(jié)點。
      • whatToShow(可選):一個整數(shù),表示要顯示的節(jié)點類型。默認值是NodeFilter.SHOW_ALL,表示顯示所有節(jié)點。
      • filter(可選):一個 NodeFilter 對象,用于自定義過濾邏輯。
      • entityReferenceExpansion(可選):一個布爾值,表示是否展開實體引用。這個參數(shù)在現(xiàn)代瀏覽器中通常被忽略,因為實體引用在 HTML 中很少使用
      const walker = document.createTreeWalker(
          document.body,//.root
          NodeFilter.SHOW_ELEMENT,// whatToShow(可選)
          null,// filter(可選)
          false //entityReferenceExpansion(可選)
      )
      

      NodeFilter.SHOW_ELEMENT 表示顯示元素節(jié)點。

      節(jié)點類型

      NodeFilter 有 12 種節(jié)點類型,和 Node 接口的節(jié)點類型一一對應(yīng);

      NodeFilter Node.prototype
      SHOW_ELEMENT:顯示元素節(jié)點。 1: ELEMENT_NODE
      SHOW_ATTRIBUTE:顯示屬性節(jié)點(在HTML 中不常用)。 2: ATTRIBUTE_NODE
      SHOW_TEXT:顯示文本節(jié)點。 3:TEXT_NODE
      SHOW_CDATA_SECTION:顯示CDATA 節(jié)點(在HTML 中不常用)。 4:CDATA_SECTION_NODE
      SHOW_ENTITY_REFERENCE:顯示實體引用節(jié)點(在HTML 中不常用)。 5: ENTITY_REFERENCE_NODE
      SHOW_ENTITY:顯示實體節(jié)點(在HTML 中不常用)。 6 : ENTITY_NODE
      SHOW_PROCESSING_INSTRUCTION:顯示處理指令節(jié)點。 7: PROCESSING_INSTRUCTION_NODE
      SHOW_COMMENT:顯示注釋節(jié)點。 8:COMMENT_NODE
      SHOW_DOCUMENT:顯示文檔節(jié)點。 9:DOCUMENT_NODE
      SHOW_DOCUMENT_TYPE:顯示文檔類型節(jié)點。 10: DOCUMENT_TYPE_NODE
      SHOW_DOCUMENT_FRAGMENT:顯示文檔片段節(jié)點。 11 : DOCUMENT_FRAGMENT_NODE
      SHOW_NOTATION:顯示符號節(jié)點(在HTML 中不常用)。 12 : NOTATION_NDE

      NodeFilter.SHOW_ALL 表示顯示所有類型節(jié)點,這和遍歷節(jié)點的 childNodes 一樣,childNodes 會把該節(jié)點下的所有類型的子節(jié)點遍歷出來。而節(jié)點的 children 就只遍歷元素節(jié)點。

      自定義過濾器

      可以通過傳遞一個 NodeFilter 對象來實現(xiàn)自定義的過濾邏輯。NodeFilter 對象有一個 acceptNode 方法,該方法返回一個常量來決定是否接受節(jié)點:

      • NodeFilter.FILTER_ACCEPT:接受節(jié)點。
      • NodeFilter.FILTER_REJECT:拒絕節(jié)點及其子節(jié)點。
      • NodeFilter.FILTER_SKIP:跳過節(jié)點,但繼續(xù)遍歷其子節(jié)點。
      const filter = {
          acceptNode: function (node){
              if (node.tagName=== "DIV"){
                  return NodeFilter.FILTER_ACCEPT;
              }else {
                  return NodeFilter.FILTER_SKIP;
              }
          },
      };
      
      const walker = document.createTreeWalker(
          document.body,
          NodeFilter.SHOW_ELEMENT,
          filter,//只遍歷標簽名是div的元素
          false
      );
      let node;
      while ((node = walker.nextNode())!== nu11){
      console.log(node);
      
      遍歷節(jié)點

      TreeWalker 提供了多種方法來遍歷節(jié)點:

      nextNode():移動到下一個節(jié)點,如果沒有更多節(jié)點則返回 null。

      • previousNode():移動到上一個節(jié)點,如果沒有更多節(jié)點則返回 null。
      • parentNode():移動到當(dāng)前節(jié)點的父節(jié)點,如果沒有父節(jié)點則返回 null。
      • firstChild():移動到當(dāng)前節(jié)點的第一個子節(jié)點,如果沒有子節(jié)點則返回 null。
      • lastChild():移動到當(dāng)前節(jié)點的最后一個子節(jié)點,如果沒有子節(jié)點則返回 null。
      • nextSibling():移動到當(dāng)前節(jié)點的下一個兄弟節(jié)點,如果沒有更多兄弟節(jié)點則返回 null。
      • previousSibling():移動到當(dāng)前節(jié)點的上一個兄弟節(jié)點,如果沒有更多兄弟節(jié)點則返回 null。

      需要注意的是,nextNode()是深度優(yōu)先遍歷

      當(dāng)前節(jié)點

      TreeWalker 對象有一個 currentNode 屬性,表示當(dāng)前遍歷到的節(jié)點,這個屬性是可讀寫的,可以通過這個屬性來獲取或設(shè)置當(dāng)前節(jié)點。

      console.log(walker.currentNode);// 輸出當(dāng)前節(jié)點
      //設(shè)置當(dāng)前節(jié)點
      walker.currentNode = document.getElementById("id");
      console.log(walker.currentNode);//輸出新設(shè)置的當(dāng)前節(jié)點
      
      實踐并和 querySelector() 比較

      querySelector() 是一個選擇器通過傳入靜態(tài)的 css 選擇器獲取元素。

      而 TreeWalker 會創(chuàng)建一個對象,適應(yīng)于進行復(fù)雜的 DOM 操作的場景,在遍歷過程中支持添加、刪除或修改節(jié)點,或者動態(tài)改變遍歷方向,很靈活。

      這兩個本來就是適用于不同場景,獲取元素基本上還是用querySelector(),不過涉及到復(fù)雜循環(huán)遍歷時就可以考慮 TreeWalker 了。

      這里我測試了一下,在怎樣的復(fù)雜程度下,TreeWalker 遍歷 會比用 for 循環(huán) + querySelector() 遍歷執(zhí)行速度上更快。

      經(jīng)過不斷測試,在循環(huán)嵌套遍歷 1000 個元素時,并且對每個元素進行添加刪除子元素的操作,此時使用 TreeWalker 遍歷執(zhí)行速度更快。這 1000 個數(shù)量并不是一個可以判定復(fù)雜程度確定的值,只是在當(dāng)前瀏覽器下測試出來的一個大概的數(shù)量。
      因為這還與對元素操作復(fù)雜度有關(guān),與瀏覽器執(zhí)行性能也有關(guān),隨著瀏覽器不斷更新迭代,后面應(yīng)該只會越來越快。

      下面整理下測試過程,在頁面中創(chuàng)建了一個 id是root的元素

      <div id="root"></div>
      <style>
          #root>div{margin-bottom: 20px;}
          .ColDiv{display: flex;gap: 10px;}
      </style>
      

      然后給 root 創(chuàng)建1000個子元素,這里使用了三重 for 循環(huán)js

      function createEl(el) {
        var fragment = document.createDocumentFragment();
        for (var i = 0; i < 10; i++) {
          var divBox = document.createElement("div");
          var innerHTML = `Row${i}`;
          for (let j = 0; j < 10; j++) {
            innerHTML += `<div><div class="ColDiv">Col${j}=>`;
            for (let k = 0; k < 10; k++) {
              innerHTML += `<div>children${k}</div>`;
            }
            innerHTML += `</div>`;
          }
          divBox.innerHTML = innerHTML;
          fragment.appendChild(divBox);
          el.appendChild(fragment);
        }
      }
      createEl(document.getElementById("root"));
      

      渲染到頁面上就是這樣,截圖沒有全部截完:
      image

      然后用循環(huán) + querySelector 遍歷 root,這里為了讓遍歷復(fù)雜一點,添加了一個邏輯:當(dāng)遍歷到子節(jié)點是 children2 時,
      給這個節(jié)點添加一個新的子節(jié)點,然后又刪除它;最后計算執(zhí)行時間;

      const querySelectorTest = () => {
          let root = document.querySelector("#root");
          let children = root.children;
          let len = children.length;
          console.time("querySelector");
          const tempFn = (list) => {
              for (let i = 0; i < list.length; i++) {
                  let node = list[i];
                  if (node.textContent==="children2") {
                      //添加一個新的子節(jié)點
                      const newDiv = document.createElement("div");
                      newDiv.textContent = "New Item";
                      node.appendChild(newDiv);
                      console.log("Added new node:");
                      //刪除添加的子節(jié)點
                      node.removeChild(newDiv);
                  }
                  if (node.children.length) {
                      tempFn(node.children);
                  }
              }
          }
          tempFn(children);
          console.timeEnd("querySelector");
      }
      

      然后同樣的邏輯,用 TreeWalker 來遍歷

      const TreeWalkerTest = () => {
          const walker = document.createTreeWalker(
              document.getElementById("root"),
              NodeFilter.SHOW_ELEMENT,
              null,
              false
          );
          console.time("treeWalker");
          let node;
          while ((node = walker.nextNode()) !== null) {
              if (node.textContent === "children2") {
                  //添加一個新的子節(jié)點
                  const newDiv = document.createElement("div");
                  newDiv.textContent = "New Item";
                  node.appendChild(newDiv);
                  //移動到新添加的節(jié)點
                  let newNode = walker.nextNode();
                  console.log("Added new node:");
                  //刪除一個節(jié)點前需要先移動到上一個節(jié)點 walker.previousNode(),這樣才能順利遍歷下一個;
                  walker.previousNode();
                  newNode.parentNode.removeChild(newNode);
              }
          }
          console.timeEnd("treeWalker");
      }
      

      這里需要注意的是,刪除一個節(jié)點前需要先移動到上一個節(jié)點 walker.previousNode(),這樣才能順利遍歷下一個;

      同時測試這兩個函數(shù)

      for (let i = 0; i < 10; i++) {
        TreeWalkerTest()
        querySelectorTest()
      }
      

      結(jié)果如下:
      image-1

      可以看到多次運行測試函數(shù),TreeWalker 執(zhí)行速度大多數(shù)都更快;

      然后修改 root 子元素數(shù)量試試,從1000改為100,測試函數(shù)的邏輯不變;

      function createEl(el) {
        var fragment = document.createDocumentFragment();
        // for (var i = 0; i < 10; i++) {
          var divBox = document.createElement("div");
          var innerHTML = `Row`;
          for (let j = 0; j < 10; j++) {
              innerHTML += `<div><div class="ColDiv">Col${j}=>`;
              for (let k = 0; k < 10; k++) {
                  innerHTML += `<div>children${k}</div>`;
              }
              innerHTML += `</div>`;
          }
          divBox.innerHTML = innerHTML;
          fragment.appendChild(divBox);
          el.appendChild(fragment);
        // }
      }
      

      再來測試下:
      image-2

      TreeWalker 執(zhí)行速度依然大多數(shù)都更快;

      再來修改下測試函數(shù)的邏輯,只遍歷,不進行添加刪除節(jié)點的操作

      const querySelectorTest = () => {
          let root = document.querySelector("#root");
          let children = root.children;
          let len = children.length;
          console.time("querySelector");
          const tempFn = (list) => {
              for (let i = 0; i < list.length; i++) {
                  let node = list[i];
                  // if (node.textContent === "children2") {
                  // 	//添加一個新的子節(jié)點
                  // 	const newDiv = document.createElement("div");
                  // 	newDiv.textContent = "New Item";
                  // 	node.appendChild(newDiv);
                  // 	// console.log("Added new node:");
                  // 	//刪除添加的子節(jié)點
                  // 	node.removeChild(newDiv);
                  // }
                  if (node.children.length) {
                          tempFn(node.children);
                  }
              }
          }
          tempFn(children);
          console.timeEnd("querySelector");
      }
      
      const TreeWalkerTest = () => {
      	const walker = document.createTreeWalker(
      		document.getElementById("root"),
      		NodeFilter.SHOW_ELEMENT,
      		null,
      		false
      	);
      	console.time("treeWalker");
      	let node;
      	while ((node = walker.nextNode()) !== null) {
      		// if (node.textContent === "children2") {
      		// 	//添加一個新的子節(jié)點
      		// 	const newDiv = document.createElement("div");
      		// 	newDiv.textContent = "New Item";
      		// 	node.appendChild(newDiv);
      		// 	//移動到新添加的節(jié)點
      		// 	let newNode = walker.nextNode();
      		// 	// console.log("Added new node:");
      		// 	//刪除一個節(jié)點
      		// 	walker.previousNode();
      		// 	newNode.parentNode.removeChild(newNode);
      		// }
      	}
      	console.timeEnd("treeWalker");
      }
      
      for (let i = 0; i < 10; i++) {
      	TreeWalkerTest()
      	querySelectorTest()
      }
      

      結(jié)果如下:
      image-3

      TreeWalker 執(zhí)行速度還是大多數(shù)都更快;

      但其實這里測試意義不大了,這個例子實際上是在測試 while 循壞for 循環(huán)+遞歸 的差異了;單論循環(huán)而言, while 循環(huán)總是最快的;

      那么接下來把 root 子節(jié)點打平,不再嵌套了,也就是遍歷一維數(shù)組,然后把 querySelectorTest 的 for 循環(huán)改為 while 循環(huán),再來試一下

      function createEl(el, len) {
      	var fragment = document.createDocumentFragment();
      	for (var i = 0; i < 10000; i++) {
      		var divBox = document.createElement("div");
      		divBox.innerHTML = "Row" + i;
      		fragment.appendChild(divBox);
      	}
      	el.appendChild(fragment);
      }
      const TreeWalkerTest = () => {
      	const walker = document.createTreeWalker(
      		document.getElementById("root"),
      		NodeFilter.SHOW_ELEMENT,
      		null,
      		false
      	);
      	console.time("treeWalker");
      	let node;
      	while ((node = walker.nextNode()) !== null) {}
      	console.timeEnd("treeWalker");
      };
      const querySelectorTest = () => {
      	let root = document.querySelector("#root");
      	let children = root.children;
      	let len = children.length;
      	console.time("querySelector");
      	let i = 0;
      	while (i++ < len) { }
      	console.timeEnd("querySelector");
      }
      

      這樣就是普通的兩個 while 循環(huán)對比了,此時 TreeWalker 就沒有優(yōu)勢了。

      image-4

      總結(jié)起來,在不復(fù)雜的場景下,遍歷的元素數(shù)量不多或者嵌套層級不深,或者對遍歷的元素沒有進行復(fù)雜的DOM操作,使用普通 for 循環(huán),while 循環(huán)操作元素始終比 TreeWalker 快,
      反之可以考慮使用 TreeWalker

      posted @ 2025-08-02 12:07  xingba-coder  閱讀(167)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 99精品国产一区二区三区不卡| 亚洲男人精品青春的天堂| 亚洲综合精品一区二区三区| 无码国产精品一区二区免费3p| 天美麻花果冻视频大全英文版| 熟女在线视频一区二区三区 | 兴化市| 中文字幕国产精品第一页| 国产一级区二级区三级区| 国产成人无码一区二区三区在线| 久久国产精品亚洲精品99| 一区二区三区国产不卡| 免费看欧美全黄成人片 | 日本偷拍自影像视频久久| 日韩人妻无码精品系列| 国产精品不卡一区二区三区| 亚洲国产成人久久综合野外| 国产精品乱子乱xxxx| 丰满巨乳淫巨大爆乳| 国精品午夜福利视频不卡| 日韩人妻中文字幕精品| 亚洲乱码中文字幕综合| 精品一区二区三区在线成人 | 7878成人国产在线观看| 福利视频在线播放| 亚洲AV日韩精品久久久久| 亚洲欧美日韩在线码| 韩国免费a级毛片久久| 日韩av一区二区三区不卡| 美女内射无套日韩免费播放| 亚洲av成人一区在线| 亚洲av色在线播放一区| 人人妻人人添人人爽日韩欧美| 亚洲av日韩av永久无码电影| 精品人妻码一区二区三区| 日韩一区二区三区不卡片| 国产亚洲一二三区精品| 昌邑市| 国产欧美久久一区二区| 激情内射亚州一区二区三区爱妻| 国产精品无码素人福利不卡|