小熊奶糖(BearCandy)
小熊奶糖(BearCandy)
发布于 2025-10-05 / 4 阅读
0
0

HTMLCollection 与 NodeList 的对比

核心概念分解

1. 节点 (Node)

在文档对象模型 (DOM) 中,“节点”是一个基础且广泛的概念。DOM 树中的所有内容都可以被视为一个节点。

节点有很多类型,最常见的几种包括:

  • 元素节点 (NodeType = 1): 例如 <div>, <p>, <span>, <body> 等 HTML 标签。
  • 属性节点 (NodeType = 2): 例如 id="title", class="active" 等元素的属性。(注意:在现代DOM操作中,属性节点通常不被视为DOM树的一部分,它们通过 attributes 属性访问)
  • 文本节点 (NodeType = 3): 元素节点内的纯文本内容,包括换行和空格。例如 <p>这是一个文本节点</p> 中的“这是一个文本节点”。
  • 注释节点 (NodeType = 8): HTML中的注释 <!-- 这是一个注释 -->
  • 文档节点 (NodeType = 9): 整个文档 (document)。

简单来说:节点是构成DOM树的任何类型的对象。

2. 元素 (Element)

“元素”是“节点”的一个特定子集。它特指那些由 HTML 标签(如 <div>, <p>)创建的元素节点

简单来说:所有元素都是节点,但并非所有节点都是元素(比如文本节点、注释节点就不是元素)。


HTMLCollection 与 NodeList 的对比

现在我们来理解这两个集合对象,它们都是“类数组”的对象集合,可以通过索引访问,但存在关键区别。

特性 HTMLCollection NodeList
包含的内容 仅包含元素节点 (Element Nodes) 包含任意类型的节点 (Element, Text, Comment 等)
如何获取 一些返回“动态集合”的旧方法:
document.getElementsByClassName()
document.getElementsByTagName()
element.children
大部分现代方法:
document.querySelectorAll() (返回静态集合)
element.childNodes (返回动态集合)
动态性 永远是动态的 (Live) 大部分是静态的 (Static),除了 childNodes
提供的方法 方法很少,主要通过索引或 id/name访问。 提供了 forEach() 方法,可以像数组一样遍历。

关键区别详解

1. 内容区别 (元素 vs. 节点)

假设我们有如下HTML:

<div id="container">
  我是一个文本节点。
  <p class="item">我是一个段落元素</p>
  <!-- 我是一个注释节点 -->
</div>
  • 获取 HTMLCollection (例如 element.children):

    let container = document.getElementById('container');
    let htmlCollection = container.children;
    console.log(htmlCollection); 
    // 输出: HTMLCollection [ <p.item> ]
    // 只包含 <p> 这个元素节点。
    
  • 获取 NodeList (例如 element.childNodes 或 `document.querySelectorAll(‘*’)):

    let container = document.getElementById('container');
    let nodeList = container.childNodes;
    console.log(nodeList); 
    // 输出: NodeList [ #text "我是一个文本节点。", <p.item>, #comment "我是一个注释节点" ]
    // 包含文本节点、元素节点和注释节点。
    
    // querySelectorAll 返回的 NodeList 只包含元素
    let nodeList2 = document.querySelectorAll(‘*’); // 只包含所有元素节点
    

2. 动态性区别 (动态 vs. 静态)

这是另一个极其重要的区别。

  • 动态集合 (Live Collection): 当DOM结构发生变化时,集合会自动更新以反映这些变化。
  • 静态集合 (Static Collection): 集合在创建的那一刻就固定了,之后DOM的变化不会影响它。

示例:

<div id="myDiv">
  <p>段落1</p>
</div>
// 1. 获取 HTMLCollection (动态)
let liveCollection = document.getElementsByTagName('p');
console.log(liveCollection.length); // 输出: 1

// 2. 获取 NodeList (静态)
let staticNodeList = document.querySelectorAll('p');
console.log(staticNodeList.length); // 输出: 1

// 现在,我们添加一个新的 <p> 元素
let newP = document.createElement('p');
newP.textContent = '段落2';
document.getElementById('myDiv').appendChild(newP);

// 再次检查长度
console.log(liveCollection.length);     // 输出: 2 (自动更新了!)
console.log(staticNodeList.length);     // 输出: 1 (保持不变)

注意: NodeList 并非全是静态的。element.childNodes 返回的就是一个动态的 NodeList。


总结与记忆技巧

概念 一句话总结
节点 (Node) DOM树中的任何东西(元素、文本、注释等)。
元素 (Element) 由HTML标签定义的特定类型的节点(如 <div>)。
HTMLCollection 只包含元素动态集合。
NodeList 包含各种类型节点的集合,通常是静态的(除了 childNodes)。

实践建议:

  • 当你只需要操作元素,并且希望集合能实时反映DOM变化时,使用 HTMLCollection (如 element.children)。
  • 当你需要获取一个不会改变的快照,或者需要操作非元素节点时,使用 document.querySelectorAll() 返回的静态 NodeList。它的 forEach 方法也让遍历更方便。
  • 使用 Array.from() 可以将它们转换为真正的数组,以便使用 map, filter, reduce 等强大的数组方法。

当他的值变化就比如说input标签或者其他时哪一个能获取到新的内容,哪一个不能?

核心结论

  • 动态集合 (HTMLCollection 和部分 NodeList)实时获取到新的内容
  • 静态集合 (querySelectorAll 返回的 NodeList)不能获取到新的内容

具体示例

1. HTMLCollection (动态 - 能获取新内容)

<input type="text" id="myInput" value="初始值">
<div id="output1"></div>
<div id="output2"></div>

<script>
// 获取 HTMLCollection
const inputs = document.getElementsByTagName('input');
const output1 = document.getElementById('output1');

// 初始显示
output1.textContent = `初始值: ${inputs[0].value}`;

// 当输入框值变化时,HTMLCollection 能实时获取新值
document.getElementById('myInput').addEventListener('input', function() {
    output1.textContent = `实时获取: ${inputs[0].value}`; // inputs[0] 始终指向当前元素
});
</script>

2. NodeList from querySelectorAll (静态 - 不能获取新内容)

<input type="text" id="myInput" value="初始值">
<div id="output2"></div>

<script>
// 获取静态 NodeList
const staticInputs = document.querySelectorAll('input');
const output2 = document.getElementById('output2');

// 初始显示
output2.textContent = `初始值: ${staticInputs[0].value}`;

// 当输入框值变化时,静态 NodeList 不能获取新值
document.getElementById('myInput').addEventListener('input', function() {
    // 这里 staticInputs[0] 保存的是创建时的快照,不会更新
    output2.textContent = `静态获取: ${staticInputs[0].value}`; // 永远显示"初始值"
  
    // 正确做法:重新查询或直接使用 this
    // output2.textContent = `重新查询: ${document.querySelector('input').value}`;
    // 或者: output2.textContent = `使用this: ${this.value}`;
});
</script>

3. 实时对比演示

<input type="text" id="testInput" placeholder="输入内容测试">
<button onclick="testCollections()">测试集合</button>
<div>
    <p>HTMLCollection (动态): <span id="dynamicResult"></span></p>
    <p>NodeList (静态): <span id="staticResult"></span></p>
    <p>直接访问: <span id="directResult"></span></p>
</div>

<script>
const dynamicCollection = document.getElementsByTagName('input');
const staticCollection = document.querySelectorAll('input');

function testCollections() {
    const input = document.getElementById('testInput');
  
    document.getElementById('dynamicResult').textContent = 
        dynamicCollection[0].value; // 能获取新值
  
    document.getElementById('staticResult').textContent = 
        staticCollection[0].value; // 不能获取新值
  
    document.getElementById('directResult').textContent = 
        input.value; // 直接访问,当然能获取新值
}

// 实时监听
document.getElementById('testInput').addEventListener('input', testCollections);
</script>

总结表格

集合类型 方法示例 动态性 能否获取值变化
HTMLCollection getElementsByTagName() 动态
HTMLCollection getElementsByClassName() 动态
HTMLCollection element.children 动态
NodeList element.childNodes 动态
NodeList document.querySelectorAll() 静态 不能

最佳实践建议

  1. 需要实时监控变化:使用动态集合或直接引用元素
  2. 只需要一次性快照:使用 querySelectorAll
  3. 处理表单值变化:推荐直接使用事件对象或元素引用
// 推荐做法
inputElement.addEventListener('input', function(e) {
    console.log(this.value);        // 推荐
    console.log(e.target.value);    // 推荐
});

// 或者在需要时重新查询
console.log(document.querySelector('input').value);

关键记住querySelectorAll 返回的是创建时刻的快照,不会自动更新;而 getElementsBy* 方法返回的是实时视图,会反映DOM的当前状态。


评论