JavaScript | HTMLCollection与NodeList

前言:今天写测试demo的时候发现了一个有趣的现象,比如在查找dom选择器的时候如果使用了通用选择器querySelectorAll()返回的是NodeList对象,而使用parentNode.children返回的确是一个HTMLCollection对象,所以想记下二者之间的区别与相同点。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<div id="father">
<div id="son1">son1</div>
<div id="son2">son2</div>
</div>
<script>
let father = document.getElementById('father');
let sons = document.querySelectorAll('div[id^=son]');
let divs = document.getElementsByTagName('div');
// NodeList
console.log(sons);
console.log(father.childNodes);
// HTMLCollection
console.log(father.children);
console.log(divs)
</script>

HTMLCollection对象

HTMLCollection 接口表示一个包含了元素(元素顺序为文档流中的顺序)的通用集合(generic collection),还提供了用来从该集合中选择元素的方法和属性。——MDN—HTMLCollection

属性:

  • HTMLCollection.length(只读):返回集合当中子元素的数目。

方法:

  • HTMLCollection.item(i):根据传入的索引值,返回具体的节点。如果节点不存在,则返回null

  • HTMLCollection.namedItem(str):根据穿入的字符串,作为关键字,先根据关键字先查找对应id的元素,如果元素不存在则根据字符串所表示的 name 属性来匹配。

    测试代码:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    <div id="father">
      <div id="son1">son1</div>
      <div id="son2" name="son1">son2</div>
      <div name="son3">son3</div>
    </div>
    <script>  
      let divs = document.getElementsByTagName('div');
      // 获取HTMLCollection的长度
      console.log(divs.length);
      // true
      console.log(divs instanceof HTMLCollection);
      // divs[2].innerText = 'nmsl'
      console.log(divs.item(0));
      let degreeElement = divs.namedItem('son3')
    </script>
    

注意:

  • HTMLCollection对象,虽然可以通过数组下标访问元素,但是不可以使用数组对象的方法。

NodeList对象

NodeList 对象是一个节点的集合,是由 Node.childNodes 和 document.querySelectorAll 返回的.——MDN—NodeList

属性:

  • NodeList.length:NodeList 中包含的节点个数。

方法:

  • NodeList.item():根据传入的索引值,返回具体的节点。如果节点不存在,则返回null。与nodeList[i]等价,如果此时越界返回undefined
  • NodeList.forEach():用于遍历NodeList中的所有成员。其使用和数组对象的forEach方法完全一样。
  • NodeList.keys()/values()/entries()keys() 返回键名的遍历器,values() 返回键值的遍历器,entries() 返回的遍历器同时包含键名和键值的信息。

测试用例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
let divs = document.querySelectorAll('div');
// console.log(divs.item(1));  //<div id="son1">son1</div>
for (const key of divs.keys()) {
    console.log(divs[key]);
}

for (const value of divs.values()) {
    console.log(value);
}

for (const entry of divs.entries()) {
    console.log(entry); 
}

测试结果如图所示:

截图

大部分时候NodeList是一个静态集合,可以将其理解做给网页上符合条件的DOM节点拍下了一张快照,也就意味着随后对文档对象模型的任何改动都不会影响集合的内容。比如说比如 document.querySelectorAll 就会返回一个静态 NodeList

可以看下下面这个案例理解一下,代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<div id="father">
  <div id="son1">son1</div>
  <div id="son2" name="son1">son2</div>
</div>
<script>
  let father = document.getElementById('father');
  let sons = document.querySelectorAll('div[id^=son]');
  // NodeList
  // 1. 首先页面上有3个div元素,现在通过querySelectorAll得到的NodeList为3
  let divs = document.querySelectorAll('div');
  // 初始长度为3
  console.log(divs.length);
  
  // 2. 通过removeChild删除son1
  divs[0].removeChild(divs[0].children[0]);
  
  // 3. 删除了id为son1的元素后,divs的长度依旧为3
  console.log(divs.length);
  
  // 4. 向id为father的元素里面追加了一个新的元素
  let newSon = document.createElement('div');
  newSon.setAttribute('id','son3');
  newSon.innerText = 'son3';
  divs[0].appendChild(newSon);
  // 5. 在向id为father的元素里面追加了一个新的元素之后,divs的长度依旧为 3
  console.log(divs.length);
  // 6. 将father的第一个子孩子删掉
  father.removeChild(father.firstElementChild);
  // 7. 重新获取div的NodeList长度 2 而之前的divs的长度依旧为 3
  console.log(document.querySelectorAll('div').length, divs.length);
</script>

在一些情况下,NodeList 是一个 实时集合 ,也就是说,如果文档中的节点树发生变化,NodeList 也会随之变化,比如,Node.childNodes 就是是实时的:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<div id="father">
    <div id="son1">son1</div>
    <div id="son2" name="son1">son2</div>
</div>
<script>
    let divs = document.querySelectorAll('div');
    var father = document.getElementById('father');
    var child_nodes = father.childNodes;
    console.log(child_nodes); // NodeList(5)

    console.log(child_nodes.length); // 我们假设结果会是5
    // 给father添加一个子节点
    let newDiv = document.createElement('div');
    newDiv.innerHTML = '114514';
    father.appendChild(newDiv);
    console.log(child_nodes.length); // 但此时的输出是6
</script>

HTMLCollection 与 NodeList 的相同点与不同点

不同点

  1. HTMLCollection是个元素的集合,不包含文本、注释节点等。
  2. NodeList是节点的集合,可以包含文本、注释节点。
  3. 大部分时候 NodeList 是一个静态的结合,不受DOM树的影响,相当于DOM树的快照,节点数量和类型的快照,就是对节点增删,NodeList 感觉不到,但是对节点内部内容修改,是可以感觉到的,比如修改 innerHTML
  4. HTMLCollection 是动态绑定的,是一个的动态集合,DOM 树发生变化,HTMLCollection 也会随之变化,节点的增删是敏感的。
  5. HTMLCollection 元素可以通过 name,idnameItem方法) 或 index(item方法) 索引来获取。NodeList 只能通过 indexitem方法) 索引来获取。
  6. 二者都无法使用数组的方法,除非转为一个数组。
    1. NodeList的原型链:myNodeList --> NodeList.prototype --> Object.prototype --> null
    2. HTMLCollection的原型链:myHTMLCollection --> HTMLCollection.prototype --> Object.prototype --> null

相同点

  1. 都是伪数组结构的数据,都有length属性
  2. 都拥有item()方法获取,集合内对应索引号的元素

参考文献