跟着MDN学HTML_day_46:(HTMLCollection与NodeList)

跟着 MDN 学 HTML day_46:深入理解 HTMLCollection 与 NodeList


📑 目录

左列章节 右列章节
[一、HTMLCollection 接口概述](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点) [二、HTMLCollection.length 属性](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点)
[三、HTMLCollection.item() 方法](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点) [四、HTMLCollection.namedItem() 方法](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点)
[五、HTMLCollection 的即时更新特性](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点) [六、HTMLCollection 属性的特殊访问方式](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点)
[七、NodeList 与 HTMLCollection 的区别](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点) [八、NodeList.item() 方法](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点)
[九、遍历方式对比](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点) [十、实际应用场景与最佳实践](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点)
[十一、总结与实践要点](#左列章节 右列章节 一、HTMLCollection 接口概述 二、HTMLCollection.length 属性 三、HTMLCollection.item() 方法 四、HTMLCollection.namedItem() 方法 五、HTMLCollection 的即时更新特性 六、HTMLCollection 属性的特殊访问方式 七、NodeList 与 HTMLCollection 的区别 八、NodeList.item() 方法 九、遍历方式对比 十、实际应用场景与最佳实践 十一、总结与实践要点)

一、HTMLCollection 接口概述

HTMLCollection 接口表示一个包含了元素的通用集合,这些元素按照文档流中的顺序排列。它是一个与 arguments 相似的类数组对象,提供了用来从集合中选择元素的方法和属性。

由于历史原因,DOM4 之前实现该接口的集合只能包含 HTML 元素,因此这个接口被命名为 HTMLCollection。实际上,HTMLCollection 在 HTML DOM 中具有一个非常重要的特性:它是即时更新的。当其所包含的文档结构发生改变时,这个集合会自动更新,反映最新的 DOM 状态。

代码示例:获取并验证 HTMLCollection
javascript 复制代码
// 获取页面中的所有 div 元素
const divs = document.getElementsByTagName("div");
console.log(divs instanceof HTMLCollection); // true
console.log(divs.length); // 当前 div 元素的数量

核心结论 :正是由于 HTMLCollection 是即时更新的,在对集合进行迭代的同时如果修改了 DOM 结构,可能会导致意想不到的结果。因此安全做法是在迭代前创建集合的副本

代码示例:安全迭代 ------ 先创建副本
javascript 复制代码
// 安全的做法:先创建副本再迭代
const divArray = Array.from(document.getElementsByTagName("div"));
divArray.forEach(div => {
  div.parentNode.removeChild(div); // 安全删除,因为遍历的是副本
});

⚠️ 【重点 / 面试考点】

  • HTMLCollection 是**即时更新(live)**的类数组对象,DOM 变化会自动反映到集合中
  • getElementsByTagNamegetElementsByClassNamedocument.formsdocument.images 等都返回 HTMLCollection
  • 遍历时修改 DOM 会导致跳过元素或死循环,迭代前务必创建副本
  • Array.from() 或展开运算符 [...collection] 均可将 HTMLCollection 转为静态数组

二、HTMLCollection.length 属性

length 属性返回 HTMLCollection 中元素的数量,它是一个只读的正整数。这一属性在 DOM 编程中非常常用,既可以用来判断集合是否为空,也可以作为循环的边界条件。

代码示例:length 属性的基本用法
javascript 复制代码
// 获取所有具有 .test 类的元素
const items = document.getElementsByClassName("test");
console.log("集合中元素数量:", items.length);

// 遍历集合并拼接 innerHTML
let gross = "";
for (let i = 0; i < items.length; i++) {
  gross += items[i].innerHTML;
}
console.log(gross); // 输出所有 .test 元素的 HTML 内容

length 属性在判断集合是否真实存在元素时也十分有用。与 NodeList 不同,HTMLCollection 没有 forEach 方法 ,因此传统 for 循环仍然是遍历它的主要方式。

代码示例:判断集合是否为空
javascript 复制代码
if (items.length > 0) {
  console.log("存在匹配的元素");
} else {
  console.log("没有找到任何匹配的元素");
}

三、HTMLCollection.item() 方法

item 方法根据给定的索引返回集合中对应的节点。索引从 0 开始,如果索引超出范围,则返回 null。在 JavaScript 中,更常用也更简洁的方式是使用方括号语法直接访问。

代码示例:两种索引访问方式对比
javascript 复制代码
// 获取页面中的所有图片元素
const images = document.images; // HTMLCollection
const firstImage = images.item(0);  // 使用 item 方法
const secondImage = images[1];      // 使用方括号语法

console.log(firstImage === secondImage); // false,不同的元素

两种访问方式在功能上基本等价,但在索引越界时的行为略有不同。

代码示例:索引越界时的行为差异
javascript 复制代码
const images = document.getElementsByTagName("img");
console.log(images.item(100)); // 返回 null(索引超出范围)
console.log(images[100]);      // 返回 undefined(索引超出范围)

核心结论item 方法在非 JavaScript DOM 实现中更加有用,而在 JavaScript 环境中,方括号语法因为简洁而更受开发者青睐


四、HTMLCollection.namedItem() 方法

namedItem 方法用于根据 idname 属性从集合中获取元素。它会首先尝试匹配 id ,如果未找到,则尝试匹配 name 属性。如果没有找到匹配的元素,则返回 null

代码示例:namedItem 匹配 name 属性
javascript 复制代码
// HTML 结构假设如下:
// <form id="loginForm">
//   <input name="username" type="text">
//   <input name="password" type="password">
// </form>

const form = document.getElementById("loginForm");
const usernameInput = form.children.namedItem("username");
console.log(usernameInput.type); // "text"

在 JavaScript 中,方括号语法与 namedItem 方法等价。

代码示例:方括号语法与 namedItem 等价
javascript 复制代码
const form = document.getElementById("loginForm");
const byMethod = form.children.namedItem("password");
const byBracket = form.children["password"];
console.log(byMethod === byBracket); // true

下面的完整示例展示了 namedItem 方法在实际 HTML 结构中的应用:

代码示例:namedItem 的完整应用
html 复制代码
<div id="personal">
  <span name="title">Dr.</span>
  <span name="firstname">John</span>
  <span name="lastname">Doe</span>
  <span id="degree">(MD)</span>
</div>
javascript 复制代码
const container = document.getElementById("personal");

// 通过 name 属性获取
const titleSpan = container.children.namedItem("title");

// 通过方括号语法获取(等价于 namedItem)
const firstnameSpan = container.children["firstname"];
const lastnameSpan = container.children.lastname;

// 通过 id 属性获取
const degreeSpan = container.children.namedItem("degree");

const output = document.createElement("div");
output.textContent = `Result: ${titleSpan.textContent} ${firstnameSpan.textContent} ${lastnameSpan.textContent} ${degreeSpan.textContent}`;
container.insertAdjacentElement("afterend", output);
// 输出:Result: Dr. John Doe (MD)

⚠️ 【重点 / 面试考点】

  • namedItem匹配顺序 :先匹配 id,再匹配 name,两者都找不到返回 null
  • 方括号语法 collection["name"]namedItem("name") 完全等价
  • 也可以通过点语法访问 collection.name,但受限于属性名合法性
  • HTML ID 中可能包含冒号和句点,此时必须使用方括号语法
  • HTML5 允许纯数字 ID,但 HTMLCollection 无法识别纯数字 ID(会与索引访问冲突)

五、HTMLCollection 的即时更新特性

HTMLCollection 是即时更新的,这一点在动态操作 DOM 时需要特别留意。当底层文档结构发生变化时,HTMLCollection 会自动反映这些变化,无需重新获取

代码示例:HTMLCollection 的自动更新
javascript 复制代码
const list = document.getElementById("myList");
const items = list.getElementsByTagName("li");
console.log("初始数量:", items.length); // 假设为 3

// 动态添加新的列表项
const newItem = document.createElement("li");
newItem.textContent = "新项目";
list.appendChild(newItem);

console.log("更新后数量:", items.length); // 变为 4,自动更新

这个特性虽然方便,但在遍历同时修改 DOM 结构时会引发问题。

代码示例:错误做法 ------ 遍历时删除
javascript 复制代码
const items = document.getElementsByClassName("removable");
// 错误的做法:遍历时删除会导致跳过元素
for (let i = 0; i < items.length; i++) {
  items[i].remove(); // 每次删除后 length 减小,会跳过部分元素
}

正确的做法是先将 HTMLCollection 转换为静态数组,再进行遍历和删除。

代码示例:正确做法 ------ 先转数组再操作
javascript 复制代码
const items = document.getElementsByClassName("removable");
const itemsArray = Array.from(items);
itemsArray.forEach(item => item.remove());

六、HTMLCollection 属性的特殊访问方式

HTMLCollection 会将成员的名称和索引直接以属性的形式暴露。这意味着可以通过属性名直接访问集合中的特定元素,而无需调用 itemnamedItem 方法。

代码示例:四种访问方式的等价性
javascript 复制代码
// 假设文档中有一个 id 为 myForm 的表单
const forms = document.forms; // HTMLCollection

// 以下四种方式等价
const form1 = forms[0];
const form2 = forms.item(0);
const form3 = forms.myForm;
const form4 = forms.namedItem("myForm");

console.log(form1 === form2); // true
console.log(form3 === form4); // true

需要注意的是,HTML ID 中可能包含冒号和句点这类在属性名中合法的字符,此时必须使用方括号语法进行访问。

代码示例:特殊字符 ID 的方括号访问
javascript 复制代码
// 假设存在 id 为 "named.item.with.periods" 的表单
const specialForm = document.forms["named.item.with.periods"];
console.log(specialForm);

核心结论 :HTML5 虽然允许使用纯数字的 ID,但 HTMLCollection 无法识别纯数字 ID,因为这会与数组形式的索引访问产生冲突。


七、NodeList 与 HTMLCollection 的区别

NodeList 是另一种常见的类数组集合,通常由 childNodes 属性或 querySelectorAll 方法返回。NodeList 分为两种:静态 NodeList动态 NodeList

querySelectorAll 返回的是静态 NodeList,它的内容不会随 DOM 变化而更新。

代码示例:静态 NodeList 的不变性
javascript 复制代码
const staticList = document.querySelectorAll("div");
console.log("初始数量:", staticList.length);

// 动态添加一个 div
const newDiv = document.createElement("div");
document.body.appendChild(newDiv);

console.log("更新后数量:", staticList.length); // 不变,仍是初始值

childNodes 返回的是动态 NodeList,会随 DOM 变化而更新。

代码示例:动态 NodeList 的自动更新
javascript 复制代码
const parent = document.getElementById("container");
const childList = parent.childNodes;
console.log("初始数量:", childList.length);

const newChild = document.createElement("span");
parent.appendChild(newChild);
console.log("更新后数量:", childList.length); // 增加了 1

NodeList 同样拥有 item 方法和 length 属性,但只有静态 NodeList 可以使用 forEach 方法进行遍历。

代码示例:静态 NodeList 的 forEach 遍历
javascript 复制代码
const nodeList = document.querySelectorAll("p");
nodeList.forEach(node => {
  console.log(node.textContent);
});

八、NodeList.item() 方法

NodeListitem 方法根据给定的索引返回对应的 Node 对象。索引从 0 开始,如果索引越界,该方法不会抛出异常,只会返回 null

代码示例:item 方法的索引访问
javascript 复制代码
const tables = document.getElementsByTagName("table");
const firstTable = tables.item(0);   // 第一个表格
const secondTable = tables.item(1);  // 第二个表格
const outOfRange = tables.item(999); // null

console.log(firstTable);   // 第一个 table 元素或 null
console.log(outOfRange);   // null

HTMLCollection 类似,在 JavaScript 中同样可以使用方括号语法简写。

代码示例:方括号语法的等价写法
javascript 复制代码
const tables = document.getElementsByTagName("table");
const firstTable = tables[0]; // 与 tables.item(0) 等价

核心结论item 方法是 NodeList 对象本身的方法,而不是 DOM 元素或 DOM 节点的方法。这一点在查看 API 文档时需要区分清楚。


⚠️ 【重点 / 面试考点】

对比项 HTMLCollection NodeList(静态) NodeList(动态)
获取方式 getElementsBy* / document.forms querySelectorAll 返回 childNodes 返回
是否实时更新 ✅ 即时更新(live) ❌ 静态,不随 DOM 变化 ✅ 即时更新(live)
是否支持 forEach ❌ 不支持 ✅ 支持 ✅ 支持(现代浏览器)
元素类型 Element 包含所有 Node 类型 包含所有 Node 类型
item 越界返回值 null null null
是否有 namedItem ✅ 有 ❌ 没有 ❌ 没有

九、HTMLCollection 与 NodeList 的遍历方式对比

HTMLCollectionNodeList 虽然都是类数组对象,但它们的遍历方式存在差异。

对于 HTMLCollection,由于它没有 forEach 方法,通常使用 for 循环或 for...of 循环。

代码示例:HTMLCollection 的两种遍历方式
javascript 复制代码
const collection = document.getElementsByClassName("item");

// 方式一:传统 for 循环
for (let i = 0; i < collection.length; i++) {
  console.log(collection[i].textContent);
}

// 方式二:for...of 循环
for (const element of collection) {
  console.log(element.textContent);
}

对于 NodeList,特别是 querySelectorAll 返回的静态 NodeList,可以直接使用 forEach 方法。

代码示例:NodeList 的 forEach 遍历
javascript 复制代码
const nodeList = document.querySelectorAll(".item");
nodeList.forEach((node, index) => {
  console.log(`第 ${index} 个:${node.textContent}`);
});

如果需要对 HTMLCollection 使用数组方法,可以先将其转换为真正的数组。

代码示例:转为数组后使用数组方法
javascript 复制代码
const htmlCollection = document.getElementsByClassName("item");
const array = Array.from(htmlCollection);

// 现在可以使用所有数组方法
const textContents = array.map(el => el.textContent);
const filtered = array.filter(el => el.dataset.active === "true");
array.forEach(el => console.log(el.className));

十、实际应用场景与最佳实践

理解 HTMLCollectionNodeList 的特性后,在实际开发中可以遵循以下最佳实践。

第一,选择合适的获取方法 。如果不需要动态更新,优先使用 querySelectorAll,因为它返回的静态 NodeList 更安全且支持 forEach

代码示例:推荐优先使用 querySelectorAll
javascript 复制代码
// 推荐:静态集合,不受后续 DOM 操作影响
const items = document.querySelectorAll(".item");

第二,在需要动态更新的场景下 使用 getElementsByClassNamegetElementsByTagName,但遍历时要先创建副本。

代码示例:动态集合先创建副本
javascript 复制代码
// 需要动态反映 DOM 变化时
const liveItems = document.getElementsByClassName("dynamic-item");

// 迭代前创建副本
[...liveItems].forEach(item => {
  // 安全操作
});

第三,使用 namedItem 或方括号语法 快速访问特定元素时,注意 id 优先于 name 的匹配顺序。

代码示例:通过 elements 快速访问表单控件
javascript 复制代码
const form = document.forms.myForm;
const input = form.elements["email"]; // 先匹配 id 再匹配 name

第四,在性能敏感的场景下 ,尽量减少对 HTMLCollectionlength 属性的重复访问,将其缓存到局部变量中。

代码示例:缓存 length 提升性能
javascript 复制代码
const collection = document.getElementsByTagName("div");
const len = collection.length;
for (let i = 0; i < len; i++) {
  // 对 collection[i] 进行操作
}

十一、总结与实践要点

HTMLCollectionNodeList 是 DOM 编程中频繁遇到的类数组集合,掌握它们的特性是高效操作 DOM 的基础。回顾本文的关键知识点:

  • HTMLCollection 是即时更新的类数组对象,文档结构变化会自动反映到集合中
  • length 属性返回集合元素数量,常用于判断空集合和循环控制
  • item 方法按索引获取元素,方括号语法是更简洁的替代方式
  • namedItem 方法按 idname 属性获取元素,id 优先匹配
  • 遍历 HTMLCollection 时应先创建副本,避免因即时更新导致意外行为
  • NodeList 分为静态和动态两种,querySelectorAll 返回静态,childNodes 返回动态
  • NodeListitem 方法在索引越界时返回 null
  • 静态 NodeList 支持 forEach 方法,HTMLCollection 不支持
获取方式与集合类型关系图

获取 DOM 元素集合
getElementsByTagName
getElementsByClassName
document.forms/images
querySelectorAll
childNodes
HTMLCollection

即时更新
NodeList

静态
NodeList

动态更新


✅ 文档总结

  • HTMLCollection即时更新 的类数组对象,getElementsBy* 系列方法均返回此类型
  • NodeList 分为静态querySelectorAll 返回)和动态childNodes 返回)两种
  • HTMLCollection 支持 item()namedItem()不支持 forEach ;静态 NodeList 支持 forEach
  • namedItem 匹配顺序为 id 优先于 name ,都找不到返回 null
  • 遍历时修改 DOM 会导致意外行为,务必先用 Array.from()[...] 创建副本
  • 方括号语法 collection[i]item(i) 等价,collection["name"]namedItem("name") 等价
  • 性能优化技巧:缓存 length优先使用 querySelectorAll 获取静态集合、批量操作前先转数组
  • 纯数字 ID 无法通过 HTMLCollection 的属性访问获取(与索引冲突)

完整实践示例
javascript 复制代码
// 综合应用:安全的动态元素操作
const container = document.getElementById("item-list");

// 第一步:获取静态快照(推荐做法)
const items = document.querySelectorAll(".item");

// 第二步:安全遍历,不受 DOM 变化影响
items.forEach((item, index) => {
  console.log(`处理第 ${index} 项:${item.textContent}`);
  
  // 即使在此处修改 DOM,也不会影响遍历
  if (item.dataset.remove === "true") {
    item.remove();
  }
});

// 第三步:需要动态反映时使用 HTMLCollection,但先转数组
const liveItems = container.getElementsByClassName("live-item");
const safeArray = Array.from(liveItems);

safeArray.forEach(item => {
  item.classList.add("processed");
  item.textContent += " [已处理]";
});

// 第四步:通过 namedItem 快速访问
const form = document.forms["myForm"];
const emailInput = form.elements.namedItem("email");
console.log("邮箱值:", emailInput?.value);

通过动手实践,可以更直观地体会到 HTMLCollectionNodeList 的区别,以及在不同场景下选择合适集合类型的必要性。


想要解锁更多HTML 核心标签实战、前端零基础入门干货、开发避坑全指南吗?
持续关注,后续将更新CSS 布局实战、JavaScript 交互基础、全站导航开发等硬核内容,带你从新手快速进阶,轻松搞定前端开发!

相关推荐
_Evan_Yao2 小时前
计算机大一新生如何选择方向(前端/后端/AI/运维)?
运维·前端·人工智能·后端
码途漫谈2 小时前
Scrapling:让爬虫在现代 Web 里“活下来”的自适应抓取框架
前端·爬虫·ai·开源
极梦网络无忧2 小时前
我开源了一个 Vue 3 动态表单组件库 —— real-vue3-easy-form
前端·vue.js·开源
ShyanZh2 小时前
【Claude基础】多代理协作:Agent Teams 与编排模式
前端·chrome·ai
下载居2 小时前
Google Chrome(谷歌浏览器64位) 148.0.7778
前端·chrome
MXN_小南学前端2 小时前
Vue + Quill:富文本的添加、传输、展示逻辑,以及 csReplyQuill 组件封装
前端·vue.js
XS0301062 小时前
Java Web实现简易CRUD操作笔记
java·前端·笔记
Str_Null2 小时前
Python 自动线性化 HTML/MD 表格的工程实践(一个读取表格并且提供输出的工具)
开发语言·python·html
Shadow(⊙o⊙)2 小时前
qt内详解信号和槽的基本概念+实例演示
开发语言·前端·c++·qt·学习