DOM 元素的循環遍歷

​博客地址:https://ainyi.com/89

獲取 DOM 元素的幾種方式

get 方式:

  1. getElementById
  2. getElementsByTagName
  3. getElementsByClassName
  4. getElementsByName

返回類型 HTMLCollection[]


query 方式:

  1. querySelector
  2. querySelectorAll

返回類型 NodeList[]


獲取 dom 元素的詳細介紹:https://ainyi.com/31

獲取元素

首先用兩種方式獲取元素

let a = document.getElementsByClassName('title')
let b = document.querySelectorAll('.title')

一般循環

get 方式

get 方式獲取的 dom 元素,僅可使用for-in、for-of、for循環

for(let key in a) {
  console.log(a[key])
}
// dom
// ...(每個dom元素)
// length(集合長度)
// ƒ item() { [native code] }
// ƒ namedItem() { [native code] }

其中:
ƒ item() { [native code] }
可通過 a.item(index) 獲取 dom 元素,類似 a[index]

ƒ namedItem() { [native code] }
可通過 a.namedItem('popo') 獲取 name 屬性爲 'popo' 的 dom 元素(若多個元素有相同的 name 屬性,返回第一個)

for-of、for 循環可獲取每個 dom 元素:

for(let val of a) {
  console.log(val)
}
// dom
// ...(每個dom元素)

for(let i = 0; i < a.length; i++) {
  console.log(a[i])
}
// dom
// ...(每個dom元素)

query 方式

query 方式獲取的 dom 元素,可使用forEach、for-in、for-of、for循環
forEach、for-of、for 循環的結果無差別

但 for-in 相比 get 方式 的 for-in,循環得出的結果稍有不同

for(let key in b) {
  console.log(b[key])
}
// dom
// ...(每個dom元素)
// length(集合長度)
// ƒ item() { [native code] }
// ƒ entries() { [native code] }
// ƒ forEach() { [native code] }
// ƒ keys() { [native code] }
// ƒ values() { [native code] }

與 get 方式的 for-in 相比,少了 ƒ namedItem() { [native code] },多了 Object 的幾個方法
這說明,query 方式獲取的 dom 元素集合,可執行 Object 對應的方法,但沒有 namedItem() 方法

ES6 轉換普通數組

ES6 提供了 Array.from() 方法可將這些集合轉換成普通數組,這樣就可以享用數組的各種方法了

let array = Array.from(a)

深度遍歷

節點樹的幾個屬性

  • childElementCount:返回子元素(不包括文本節點和註釋)的數量
  • parentNode:ele 的父節點
  • childNodes:ele 的所有的直接子節點
  • nextSibling:ele 的下一個同輩節點
  • previousSibling:ele 的上一個同輩節點

因爲 childNodes 包含看不見的空格文本,還有註釋等內容,所以使用起來不是太方便
因此,js 又重新引入了元素樹的概念。這個在我們實際應用中,用的比較普遍

元素樹:僅僅包含元素節點的樹結構,不是一顆新樹,盡是節點數的子集

爲元素新增了下面幾個屬性:

  • parentElement:節點的父元素
  • children:返回節點的所有子元素
  • firstElementChild:第一個直接子元素
  • lastElementChild:最後一個直接子元素
  • previousElementSibling:ele 的前一個兄弟元素
  • nextElementSibling:ele 的下個兄弟元素

一般來說,區別元素節點,屬性節點,文本節點的通用方式是判斷該節點的 nodeType

常見的幾種 nodeType:

元素節點:1,
屬性節點:2,
文本節點:3,
註釋節點:8,
...

遍歷直接子級元素

假設 html 如下,要遍歷出 div 中的所有直接子級的元素節點:

<div id="list">
  <p>hello</p>
  <span>world</span>
  <em>cookieParse()</em>
</div>

用 firstChild,lastChild 進行元素遍歷

let list = document.getElementById('list')

let child = list.firstChild

console.log(list.nextSibling)

while(child != list.lastChild) {
  if(child.nodeType === 1) {
    console.log( child )
  }
  child = child.nextSibling
}

使用 firstElementChild,nextElementSibling

let list = document.getElementById('list')

let child = list.firstElementChild

while(child) {
  console.log( child )
  child = child.nextElementSibling
}

深度優先遍歷

遍歷所有節點
深度優先遍歷:當同時有兄弟節點和子節點的時候,總是優先遍歷子節點

function getChildren(parent) {
  // 如果當前節點是元素節點,輸出當前元素
  parent.nodeType === 1 && console.log(parent);
  // 獲得父節點的所有直接子節點
  let children = parent.childNodes
  // 遍歷 children 中每個節點
  for(let i = 0, len = children.length; i<len; i++) {
    // 對當前子節點遞歸
    getChildren(children[i])
  }
}
getChildren(document.body)

需要注意的是:遞歸的運行效率沒有迭代的運行效率高,一般都需要把遞歸的循環優化成迭代的循環
所以上面遞歸算法可以進一步優化

優化深度優先遍歷

使用 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節點。對HTML頁面無用
NodeFilter.SHOW_ENTITY_REFERENCE 顯示實體引用節點
NodeFilter.SHOW_ENTITYE 顯示實體節點
NodeFilter.SHOW_PROCESSING_INSTRUCTION 顯示處理指令節點
NodeFilter.SHOW_COMMENT 顯示註釋節點
NodeFilter.SHOW_DOCUMENT 顯示元檔節點
NodeFilter.SHOW_DOCUMENT_TYPE 顯示文檔類型節點
NodeFilter.SHOW_DOCUMENT_FRAGMENT 顯示文檔片段節點
NodeFilter.SHOW_SHOW_NOTATION 顯示符號節點
NodeFilter.SHOW_DOCUMENT_TYPE 顯示文檔類型節點

優化如下:

function getChildren(parent){
  // 獲取 NodeIterator 對象
  let t = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT, null, false)
  // 循環遍歷對象的下一個節點
  let currNode = null
  while((currNode = t.nextNode()) !== null) {
    // 節點不爲空,就一直循環遍歷下去;直到爲 null,才中斷循環
    console.log(currNode)
  }
}
getChildren(document.body)

博客地址:https://ainyi.com/89

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章