Appearance
NodeList 对象
NodeList 的概念
NodeList 是一个类数组对象,用于表示 DOM 树中节点的集合。它是由 DOM 方法(如 querySelectorAll()、childNodes 等)返回的,包含了匹配指定选择器的所有节点。
NodeList 的特性
- 静态性:大多数方法返回的 NodeList 是静态的(static),意味着当文档发生变化时,它不会自动更新。但
childNodes属性返回的 NodeList 是实时的(live) - 类数组:NodeList 是类数组对象,具有
length属性,可以通过索引访问节点,但不具有所有数组的方法 - 有序性:NodeList 中的节点按照它们在文档中的顺序排列
- 多样性:NodeList 可以包含各种类型的节点(元素、文本、注释等)
NodeList 的方法和属性
length
length 属性返回 NodeList 中节点的数量:
javascript
const elements = document.querySelectorAll('.item');
console.log(elements.length); // 输出: 元素数量item()
item() 方法返回 NodeList 中指定索引的节点:
javascript
const elements = document.querySelectorAll('.item');
const firstElement = elements.item(0);
console.log(firstElement); // 输出: 第一个匹配的元素
// 与直接使用索引访问相同
const firstElement2 = elements[0];
console.log(firstElement2); // 输出: 第一个匹配的元素forEach()
forEach() 方法用于遍历 NodeList 中的所有节点:
javascript
const elements = document.querySelectorAll('.item');
elements.forEach(element => {
console.log(element.textContent);
});
// 带索引和 NodeList 的 forEach
elements.forEach((element, index, nodeList) => {
console.log(`索引 ${index}: ${element.textContent}`);
});entries()
entries() 方法返回一个迭代器,包含 NodeList 中每个节点的索引和值:
javascript
const elements = document.querySelectorAll('.item');
const iterator = elements.entries();
for (const [index, element] of iterator) {
console.log(`索引 ${index}: ${element.textContent}`);
}keys()
keys() 方法返回一个迭代器,包含 NodeList 中每个节点的索引:
javascript
const elements = document.querySelectorAll('.item');
const iterator = elements.keys();
for (const index of iterator) {
console.log(`索引: ${index}`);
}values()
values() 方法返回一个迭代器,包含 NodeList 中每个节点的值:
javascript
const elements = document.querySelectorAll('.item');
const iterator = elements.values();
for (const element of iterator) {
console.log(element.textContent);
}NodeList 的遍历
1. for 循环
使用传统的 for 循环遍历 NodeList:
javascript
const elements = document.querySelectorAll('.item');
for (let i = 0; i < elements.length; i++) {
console.log(elements[i].textContent);
}2. for...of 循环
使用 for...of 循环遍历 NodeList:
javascript
const elements = document.querySelectorAll('.item');
for (const element of elements) {
console.log(element.textContent);
}3. forEach 方法
使用 forEach() 方法遍历 NodeList:
javascript
const elements = document.querySelectorAll('.item');
elements.forEach(element => {
console.log(element.textContent);
});4. 转换为数组后遍历
将 NodeList 转换为数组后,使用数组方法遍历:
javascript
const elements = document.querySelectorAll('.item');
// 使用 Array.from() 转换
const elementsArray = Array.from(elements);
elementsArray.forEach(element => {
console.log(element.textContent);
});
// 使用扩展运算符转换
const elementsArray2 = [...elements];
elementsArray2.forEach(element => {
console.log(element.textContent);
});
// 使用数组方法
const elementsArray3 = Array.prototype.slice.call(elements);
elementsArray3.forEach(element => {
console.log(element.textContent);
});
// 使用数组的其他方法
const texts = elementsArray.map(element => element.textContent);
console.log(texts);
const longTexts = elementsArray.filter(element => element.textContent.length > 10);
console.log(longTexts);NodeList 与 HTMLCollection 的区别
| 特性 | NodeList | HTMLCollection |
|---|---|---|
| 实时性 | 大多数方法返回的是静态的(static),但 childNodes 返回的是实时的 | 实时(live) |
| 节点类型 | 包含各种类型的节点(元素、文本、注释等) | 只包含元素节点 |
| 方法 | item(), entries(), forEach(), keys(), values() | item(), namedItem() |
| 遍历 | 可以使用 for 循环、for...of 循环和 forEach 方法 | 可以使用 for 循环和 for...of 循环 |
NodeList 的应用场景
1. 静态元素操作
由于大多数方法返回的 NodeList 是静态的,它适合用于不需要动态响应文档变化的场景:
javascript
const elements = document.querySelectorAll('.item');
// 批量操作元素
elements.forEach(element => {
element.classList.add('highlight');
});
// 添加新元素不会影响 NodeList
const newItem = document.createElement('div');
newItem.className = 'item';
newItem.textContent = '新元素';
document.body.appendChild(newItem);
console.log(elements.length); // 输出: 数量不变2. 包含文本和注释节点的操作
NodeList 可以包含各种类型的节点,适合用于需要处理文本和注释节点的场景:
javascript
const container = document.getElementById('container');
const nodes = container.childNodes;
// 遍历所有子节点(包括文本和注释)
nodes.forEach(node => {
console.log(`类型: ${node.nodeType}, 名称: ${node.nodeName}, 内容: ${node.textContent}`);
});
// 过滤出元素节点
const elementNodes = Array.from(nodes).filter(node => node.nodeType === Node.ELEMENT_NODE);
elementNodes.forEach(element => {
console.log(`元素: ${element.tagName}`);
});NodeList 的最佳实践
1. 使用 forEach 方法
对于现代浏览器,使用 forEach() 方法遍历 NodeList 是最简洁的方式:
javascript
// 好的做法:使用 forEach
const elements = document.querySelectorAll('.item');
elements.forEach(element => {
console.log(element.textContent);
});
// 对于需要兼容旧浏览器的情况
if (NodeList.prototype.forEach) {
// 使用 forEach
elements.forEach(element => {
console.log(element.textContent);
});
} else {
// 使用 for 循环
for (let i = 0; i < elements.length; i++) {
console.log(elements[i].textContent);
}
}2. 转换为数组
当需要使用数组方法时,将 NodeList 转换为数组:
javascript
// 好的做法:转换为数组后使用数组方法
const elements = document.querySelectorAll('.item');
const elementsArray = Array.from(elements);
// 使用 map 方法
const texts = elementsArray.map(element => element.textContent);
console.log(texts);
// 使用 filter 方法
const longTexts = elementsArray.filter(element => element.textContent.length > 10);
console.log(longTexts);
// 使用 reduce 方法
const totalLength = elementsArray.reduce((total, element) => {
return total + element.textContent.length;
}, 0);
console.log(totalLength);3. 选择合适的 DOM 方法
根据具体需求选择合适的 DOM 方法:
- 需要静态集合:使用
querySelectorAll()(返回静态 NodeList) - 需要实时集合:使用
getElementsByClassName()或getElementsByTagName()(返回 HTMLCollection) - 需要包含所有类型的节点:使用
childNodes(返回包含所有节点类型的 NodeList) - 只需要元素节点:使用
children(返回 HTMLCollection)
4. 缓存 NodeList
避免重复查询 DOM,缓存 NodeList:
javascript
// 好的做法:缓存 NodeList
const elements = document.querySelectorAll('.item');
// 多次使用缓存的 NodeList
console.log(elements.length);
elements.forEach(element => {
console.log(element.textContent);
});
// 不好的做法:重复查询 DOM
console.log(document.querySelectorAll('.item').length);
document.querySelectorAll('.item').forEach(element => {
console.log(element.textContent);
});NodeList 的常见错误
1. 混淆静态和实时 NodeList
不同方法返回的 NodeList 可能是静态的或实时的:
javascript
// 静态 NodeList(不会自动更新)
const elements1 = document.querySelectorAll('.item');
// 实时 NodeList(会自动更新)
const container = document.getElementById('container');
const elements2 = container.childNodes;
// 添加新元素
const newItem = document.createElement('div');
newItem.className = 'item';
newItem.textContent = '新元素';
container.appendChild(newItem);
console.log(elements1.length); // 输出: 数量不变
console.log(elements2.length); // 输出: 数量增加2. 尝试使用不存在的方法
NodeList 不是数组,某些数组方法可能不存在:
javascript
const elements = document.querySelectorAll('.item');
// 错误:NodeList 没有 map 方法
// const texts = elements.map(element => element.textContent);
// 正确:转换为数组后使用
const texts = Array.from(elements).map(element => element.textContent);
console.log(texts);3. 访问不存在的索引
访问超出范围的索引会返回 null:
javascript
const elements = document.querySelectorAll('.item');
// 访问不存在的索引
const nonExistent = elements[100];
console.log(nonExistent); // 输出: null
// 安全访问
const element = elements[0] || null;
if (element) {
console.log(element.textContent);
}4. 混淆 nodeType
NodeList 包含各种类型的节点,需要注意节点类型:
javascript
const container = document.getElementById('container');
const nodes = container.childNodes;
// 错误:假设所有节点都是元素
nodes.forEach(node => {
// 可能会出错,因为文本节点没有 classList 属性
// node.classList.add('highlight');
});
// 正确:检查节点类型
nodes.forEach(node => {
if (node.nodeType === Node.ELEMENT_NODE) {
node.classList.add('highlight');
}
});小结
NodeList 是一个类数组对象,用于表示 DOM 树中节点的集合。它具有 length 属性和多个方法,如 item()、forEach()、entries()、keys() 和 values()。NodeList 可以包含各种类型的节点,大多数方法返回的 NodeList 是静态的,不会自动更新。在使用 NodeList 时,应该根据具体需求选择合适的遍历方法,并在需要时将其转换为数组以使用数组方法。