我們在閱讀JS高級程序設計的時候,提到了節點樹的概念。比如說:
elem.parentNode---找elem的父節點;
elem.childNodes---找elem的所有的直接子節點;
elem.nextSibling---找elem的下一個同輩節點‘;
elem.previousSibling---找elem的上一個同輩節點
因為childNodes包含看不見的空格文本,還有注釋等內容,所以使用起來不是太方便。
因此,JS又重新引入了元素樹的概念。這個的話,在我們實際應用中,用的比較普遍。
元素樹:僅僅包含元素節點的樹結構,不是一顆新樹,盡是節點數的子集;
elem.parentElement 找節點的父元素;
elem.children返回節點的所有子元素;
elem.firstElementChild 第一個直接子元素;
elem.lastElementChild 最后一個直接字元素;
elem.previousElementSibling 找elem的前一個兄弟元素;
elem.nextElementSibling 找elem的下個兄弟元素;
在這里,我們介紹一個筆試題:
遍歷一個指定父節點下的所有后代節點,這里需要明確一點,就是所有的后代節點都需要遍歷到:
1 <script type='text/javascript'> 2 function getChildren(parent){ //如果當前父節點不是文本節點,就輸出標簽名,否則輸出文本內容 3 console.log(parent.nodeType!=3?parent.nodeName:parent.nodeValue); //獲得父節點的所有直接子節點 4 var children = parent.childNodes; //遍歷children中每個節點 5 for(var i =0,len =children.length;i<len;i++){ //Step2: 對當前子節點調用getChildren 6 getChildren(children[i]);//這里使用遞歸的調用方法; 7 //console.log(children[i].nodeType !=3?children[i].nodeName:children[i].nodeValue); 8 } 9 } 10 getChildren(document.body); 11 </script>
這里我們需要知道一個算法-----深度優先遍歷:當同時有兄弟節點和子節點的時候,總是優先遍歷子節點。
上面代碼的遍歷順序,就是優先遍歷子節點。
我們在高性能JS這本書學到過,就是遞歸的運行效率沒有迭代的運行效率高,所以,我們優化的話,一般都需要把遞歸的循環優化成迭代的循環。
上面的例子如何修改呢,這里我們要提到一個新的概念,那就是:
NodeIterator 對象,可以對 DOM 樹進行深度優先的搜索。
創建 NodeIterator 對象,需要使用 document 對象的 createNodeIterator() 方法,該方法接收四個參數:
- root,搜索開始的節點
- whatToShow,一個數值代碼,表示哪些節點需要搜索
- filter,NodeFilter 對象,決定忽略哪些節點
- entityReferenceExpansion,布爾值,表示是否需要擴展實體引用
whatToShow 參數:
- NodeFilter.SHOW_ALL,顯示所有節點
- NodeFilter.SHOW_ELEMENT,元素節點
- NodeFilter.SHOW_ATTRIBUTE,屬性節點
- NodeFilter.SHOW_TEXT,文本節點
- NodeFilter.SHOW_CDATA_SECTION,
![CDATA]
節點 - NodeFilter.SHOW_ENTITY_REFERENCE,實體引用節點(
"
) - NodeFilter.SHOW_ENTITY,實體元素節點(
<!ENTITY foo "foo">
) - NodeFilter.SHOW_PROCESSING_INSTRUCTION,PI 節點
- NodeFilter.SHOW_COMMENT,XML 注釋節點
- NodeFilter.SHOW_DOCUMENT,文檔頂層節點
- NodeFilter.SHOW_DOCUMENT_TYPE,DTD 節點
- NodeFilter.SHOW_DOCUMENT_FRAGMNT
- NodeFilter.SHOW_NOTATION
- 我們重新修改后的代碼如下:
-
1234567891011121314
function getChildren(parent){
//獲取NodeIterator對象
var t = document.createNodeIterator(parent,NodeFilter.SHOW_ALL,null,false);
//循環遍歷對象的下一個節點;
var currNode=null;while((currNode=t.nextNode())!=null){
//節點不為空,就一直循環遍歷下去;直到為null,才中斷循環;
console.log(node.nodeType!=3?node.nodeName:node.nodeValue)
}
}
getChildren(document.body);