Skip to content

NodeList 对象

NodeList 的概念

NodeList 是一个类数组对象,用于表示 DOM 树中节点的集合。它是由 DOM 方法(如 querySelectorAll()childNodes 等)返回的,包含了匹配指定选择器的所有节点。

NodeList 的特性

  1. 静态性:大多数方法返回的 NodeList 是静态的(static),意味着当文档发生变化时,它不会自动更新。但 childNodes 属性返回的 NodeList 是实时的(live)
  2. 类数组:NodeList 是类数组对象,具有 length 属性,可以通过索引访问节点,但不具有所有数组的方法
  3. 有序性:NodeList 中的节点按照它们在文档中的顺序排列
  4. 多样性: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 的区别

特性NodeListHTMLCollection
实时性大多数方法返回的是静态的(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 时,应该根据具体需求选择合适的遍历方法,并在需要时将其转换为数组以使用数组方法。