淺談Dom遍歷

   最近找工做,看到有一道面試題是這樣的:輸出一個頁面中用到的標籤的數組。方法卻是挺多,我首先想到的就是Dom遍歷,上網看了看,你們實現Dom遍歷用到的基本上都是Dom2的方法:NodeIterator和TreeWalker,我試着以不一樣的角度談談Dom遍歷。html

1、廣度優先遍歷(BFS)
面試

廣度優先遍歷的思路定義一個隊列,用來存放子節點,以先進先出的特性,將pop出去的節點的子節點push進去,以此循環,達到BFS數組

 1 function DomBFS(element, callback) {
 2     var queue = []; //存放子節點的隊列
 3     while(element) {
 4         callback(element);
 5         if(element.children.length !== 0) {
 6             for (var i = 0; i < element.children.length; i++) {
 7                 queue.push(element.children[i]);//存放子節點
 8             }
 9         }
10         element = queue.shift(); //取出第一項
11     }
12 }

 

舉個例子,一個DOM結構是這樣的函數

 1 <div id="root">
 2     <div>
 3         <span>
 4             <p></p>
 5         </span>
 6     </div>
 7     <p>
 8         <span></span>
 9     </p>
10     <h1></h1>
11 </div>

 

1 var arr = [];//存放標籤名
2 var root = document.getElementById("root");
3 DomBFS(root, function(element) {
4     arr.push(element.tagName);
5 })
6 console.log(arr);

結果爲:spa

 

2、深度優先遍歷(DFS)3d

  深度優先遍歷的非遞歸實現:code

DFS和BFS的思路差很少,不一樣的是這裏定義了一個堆棧存放子節點,以先進後出的特性,與隊列不一樣的是,這裏須要把第一個子節點放在堆棧最上面,這樣把它pop後,再把它的子節點push進去,依次循環,達到DFShtm

 1 function DomDFS(element, callback) {
 2     var stack = [];//存放子節點的棧
 3     while (element) {
 4         callback(element);
 5         if(element.children.length !== 0) {
 6             for (var i = element.children.length - 1; i >= 0; i--) {//將最後的子節點先壓進堆棧
 7                 stack.push(element.children[i]);
 8             }
 9         }
10         element = stack.pop();
11     }
12 }

 

 還用上面的例子,結果爲:blog

 

  深度優先遍歷的遞歸實現:遞歸

1 function DomDFS(element, callback) {
2     callback(element);
3     element = element.firstElementChild;
4     while(element) {
5         DomDFS(element, callback);
6         element = element.nextElementSibling;
7     }
8 }

 

3、ES6生成器遍歷DOM樹

廣度優先遍歷和深度優先遍歷都是咱們一直在用的方法,可是也會有一點問題,就是firstElementChild在IE678下不兼容,能夠用children[0],詳細看這裏

ES6中新增的一種函數:生成器函數。用這個方法來實現遍歷DOM樹就很明瞭。若是不理解生成器,看這裏

1 function* DomTraversal (element) {
2     yield element;
3     element = element.firstElementChild;
4     while(element) {
5         yield* DomTraversal(element);
6         element = element.nextElementSibling;
7     }
8 }

 

雖說和遞歸方法很類似,但它的機制卻比遞歸簡單的多。

不一樣於在下一層遞歸處理每一個訪問過的節點子樹,這種方法能夠爲每一個訪問過的節點建立一個生成器並將執行權交給它,從而可以以迭代的方式書寫相似遞歸的代碼。

而且以一個ES6新增的for-of循環就能夠處理生成的節點。

上面的例子:

1 var arr = [];
2 var root = document.getElementById("root");
3 for (let element of DomTraversal(root)) {
4     arr.push(element.tagName);
5 }
6 console.log(arr);

 

結果和DFS同樣

相關文章
相關標籤/搜索