JS之DOM篇-節點遍歷


定義

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 // 修改起點


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM