定義
DOM遍歷模塊定義了用於輔助完成順序遍歷DOM結構的類型:Nodeiterator和TreeWalker,它們能夠基於給定的起點對DOM結構執行深度優先(depth-first)的遍歷操作
示例HTML
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
<p><b>Hello</b> world!</p>
</body>
</html>
示例HTML的DOM結構

以document為根節點的DOM樹進行深度優先遍歷的先后順序

任何節點都可以作為遍歷的根節點,示例展示了以document為根節點的DOM樹進行深度優先遍歷的先后順序,訪問的第一個節點是document節點,最后一個節點是包含'world!'的文本節點
從文檔最后的文本節點開始,遍歷可以反向移動到DOM樹的頂端,此時訪問的第一個節點是包含"Hello"的文本節點,訪問的最后一個節點是document節點。Nodeiterator和TreeWalker都以這種方式執行遍歷
NodeIterator()
document.NodeIterator()方法可以創建NodeIterator類型的新實例,該方法接收4個參數
root: 搜索起點
whatToShow: 數字代碼,表示要訪問哪些節點
filter: NodeFilter對象,或者一個表示應該接受還是拒絕某種節點的函數
entityReferenceExpansion: 布爾值,表示是否要擴展實體引用。由於在HTML頁面中,實體引用不能擴展,所以這個參數在HTML頁面中沒有用
whatToShow參數是一個位掩碼,這個參數的值以常量形式在NodeFilter類型中定義,可以通過應用一或多個過濾器(filter)來確定要訪問哪些節點
NodeFilter.SHOW_ALL: 顯示所有類型的節點
NodeFilter.SHOW_ELEMENT: 顯示元素節點
NodeFilter.SHOW_ATTRIBUTE: 顯示特性節點。由於DOM結構原因,實際上不能使用這個值
NodeFilter.SHOW_TEXT: 顯示文本節點
NodeFilter.SHOW_CDATA_SECTION: 顯示CDATA節點。對HTML頁面沒有用
NodeFilter.SHOW_ENTITY_REFERENCE: 顯示實體引用節點。對HTML頁面沒有用
NodeFilter.SHOW_ENTITYE: 顯示實體節點。對HTML頁面沒有用
NodeFilter.SH0W_PROCESSING_INSTRUCTION: 顯示處理指令節點。對HTML頁面沒有用
NodeFi1ter.SHOW_COMMENT: 顯示注釋節點
NodeFilter.SHOW_DOCUMENT: 顯示文檔節點
NodeFilter.SHOW_DOCUMENT_TYPE: 顯示文檔類型節點
NodeFilter.SHOW_DOCUMENT_FRAGMENT: 顯示文檔片段節點。對HTML頁面沒有用
NodeFilter.SHOW_NOTATION: 顯示符號節點。對HTML頁面沒有用
創建一個只顯示<p>元素的節點迭代器
var filter = {
acceptNode: function(node) {
return node.tagName.toLowerCase() === 'p' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
}
}
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false)
通過createNodeIterator()方法的filter參數,可以自定義NodeFilter對象,每個NodeFilter對象有且只有一個acceptNode()方法,如果應該訪問給定的節點,該方法返回NodeFilter.FILTER_ACCEPT,如果不應該訪問給定的節點,該方法返回NodeFilter.FILTER_SKIP。由於NodeFilter是一個抽象類型,因此不能直接創建它的實例,可以像示例中一樣,創建一個包含acceptNode()方法的對象,然后傳給createNodeIterator()方法
filter也可以是一個與acceptNode()方法類似的函數
var filter = function(node) {
return node.tagName.toLowerCase() === 'p' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
}
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ELEMENT, filter, false)
如果不指定過濾器,那么應該在第三個參數的位置上傳入null
var iterator = document.createNodeIterator(document, NodeFilter.SHOW_ALL, null, false)
NodeIterator類型有兩個主要的方法:nextNode()和previousNode()。在深度優先的DOM樹遍歷中,nextNode()方法用於向前前進一步,previousNode()用於向后后退一步
<div id="box">
<p>列表</p>
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
<script>
var iterator = document.createNodeIterator(box, NodeFilter.SHOW_ELEMENT, null, false)
var node = iterator.nextNode()
while(node !== null) {
console.log(node.tagName)
node = iterator.nextNode()
}
// DIV
// P
// UL
// LI
// LI
</script>
這個示例遍歷了box元素的所有子元素,第一調用nextNode()方法會返回<p>元素,由於在到達DOM樹末端時nextNode()返回null,所以可以用while語句檢測每次循環時node值是否等於null
如果只想獲取<li>元素,可以加一個過濾器
<div id="box">
<p>列表</p>
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
<script>
var filter = function(node) {
return node.tagName.toLowerCase() === 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
}
var iterator = document.createNodeIterator(box, NodeFilter.SHOW_ELEMENT, filter, false)
var node = iterator.nextNode()
while(node !== null) {
console.log(node.tagName)
node = iterator.nextNode()
}
// LI
// LI
</script>
由於nextNode()和previousNode()方法都基於NodeIterator在DOM結構中的內部指針工作,所以DOM結構的變化會反映在遍歷的結果中
TreeWalker
TreeWalker是NodeIterator的一個高級版本,除了包括nextNode()和previous()在內的相同功能外,還提供了用於在不同方向上遍歷DOM結構的方法
parentNode():遍歷到當前節點的父節點
firstChild():遍歷到當前節點的第一個子節點
lastChild():遍歷到當前節點的最后一個子節點
nextSibling():遍歷到當前節點的下一個同輩節點
previousSibling():遍歷到當前節點的上一個同輩節點
document.createTreeWalker()方法可以創建TreeWalker對象,它接受和document.createNodelterator()方法相同的4個參數。下面使用TreeWalker代替NodeIterator遍歷box元素節點
<div id="box">
<p>列表</p>
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
<script>
var filter = function(node) {
return node.tagName.toLowerCase() === 'li' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
}
var iterator = document.createTreeWalker(box, NodeFilter.SHOW_ELEMENT, filter, false)
var node = iterator.nextNode()
while(node !== null) {
console.log(node.tagName)
node = iterator.nextNode()
}
// LI
// LI
</script>
TreeWalker的filter參數返回值與NodeIterator的有所不同,它可以返回NodeFilter.FILTER_ACCEPT、NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT三種值。在使用TreeWalker對象時,NodeFilter.FILTER_SKIP表示跳過相應節點繼續前進到子樹中的下一個節點,NodeFilter.FILTER_REJECT表示跳過指定的節點。也就是說,在TreeWalker對象中使用NodeFilter.FILTER_REJECT與在NodeIterator對象中使用NodeFilter.FILTER_SKIP效果是相同的;在NodeIterator對象中,也可以返回NodeFilter.FILTER_REJECT,只不過NodeFilter.FILTER_SKIP和NodeFilter.FILTER_REJECT結果是一樣的
由於TreeWalker能夠在DOM結構中沿任何方向移動,所以即使不定義過濾器,也可以取得所有<li>元素
<div id="box">
<p>列表</p>
<ul>
<li>one</li>
<li>two</li>
</ul>
</div>
<script>
var walker = document.createTreeWalker(box, NodeFilter.SHOW_ELEMENT, null, false)
walker.firstChild() // 轉到p節點
walker.nextSibling() // 轉到ul節點
var node = walker.firstChild() // 轉到第一個li節點
while(node !== null) {
console.log(node.tagName)
node = walker.nextNode()
}
// LI
// LI
</script>
TreeWalker類型還有一個currentNode屬性,表示任何遍歷方法在上一次遍歷中返回的節點。通過設置這個屬性還可以修改遍歷繼續進行的起點
var node = walker.nextNode()
console.log(node === walker.currentNode) // true
walker.currentNode = document.body // 修改起點
