Skip to content

理解 JavaScript 中 `childNodes` 和 `children` 的区别及最佳实践

Posted on:2024年12月13日 at 10:46

在前端开发中,操作 DOM 是一项基本技能,而获取 DOM 的子节点则是一个非常常见的需求。JavaScript 提供了多种方法来获取子节点,比如 childNodeschildren,但它们之间的区别常常让人感到困惑。

一、问题背景

在 JavaScript 中,我们可以使用以下两种方式获取某个 DOM 元素的子节点:

  1. childNodes: 返回一个包含所有子节点的 NodeList,包括元素节点、文本节点(如空格和换行符)以及注释节点。
  2. children: 返回一个 HTMLCollection,仅包含元素节点。

例如:

let container = document.getElementById('container');
let childNodes = container.childNodes; // 包含所有子节点
let children = container.children;     // 仅包含元素节点

问题

二、childNodeschildren 的核心区别

1. childNodes

<div id="container">
  <p>Paragraph 1</p>
  <!-- Comment -->
  <p>Paragraph 2</p>
</div>

调用 childNodes 会返回:

let container = document.getElementById('container');
console.log(container.childNodes); 
// NodeList(5) [text, <p>, text, <p>, text]

2. children

console.log(container.children); 
// HTMLCollection(2) [<p>, <p>]

3. 浏览器兼容性

在早期的浏览器(例如 IE 8 及以下版本)中,childrenchildNodes 的行为有所不同:

因此,在需要兼容旧版 IE 时,需要特别注意这些差异。

三、性能比较

从性能角度来看,childNodeschildren 的差异可以忽略不计。两者的核心区别在于过滤逻辑:

由于过滤操作通常很轻量,因此性能上的差异非常微小。

四、跨浏览器兼容性处理

为了兼容旧版本的 IE 浏览器(IE < 9),我们可以使用以下方法来获取第一个子元素节点:

let firstChild = element.firstElementChild || element.firstChild;
if (firstChild && firstChild.nodeType !== 1) {
  firstChild = null; // 确保返回的节点是元素节点
}

五、最佳实践

1. 根据需求选择方法

2. 避免空白文本节点的干扰

在使用 childNodes 时,空白文本节点可能会导致问题。例如:

let container = document.getElementById('container');
let firstChild = container.childNodes[0];

console.log(firstChild); 
// 可能是一个 TextNode,而不是预期的 <p> 元素

为避免此问题,可以手动过滤掉非元素节点:

function getElementChildren(element) {
  let childNodes = element.childNodes;
  let children = [];
  for (let i = 0; i < childNodes.length; i++) {
    if (childNodes[i].nodeType === Node.ELEMENT_NODE) {
      children.push(childNodes[i]);
    }
  }
  return children;
}

3. 使用现代方法

现代浏览器支持使用 querySelectorAll 来获取直接子元素,例如:

let children = container.querySelectorAll(':scope > *');

:scope 是一种伪类,表示当前上下文中的元素。在现代浏览器中,这种方法可以替代 children

六、SVG 和特殊情况

在处理 <svg> 元素时,children 的行为可能与预期不符。例如,IE/Edge 浏览器不支持在 SVG 元素上使用 children,但 childNodes 是有效的。因此,在处理 SVG 时建议使用 childNodes

let svg = document.querySelector('svg');
let svgChildren = Array.from(svg.childNodes).filter(node => node.nodeType === Node.ELEMENT_NODE);

七、总结

childNodeschildren 是操作 DOM 子节点的两种常用方法,各有优缺点:

特性childNodeschildren
返回类型NodeListHTMLCollection
包含节点所有子节点仅限元素节点
包含文本节点
包含注释节点
浏览器兼容性支持所有浏览器(包括旧版 IE)IE < 9 不支持完全一致的行为

在实际开发中,应根据具体需求选择适合的方法,并注意兼容性问题。如果需要处理复杂的节点过滤,可以结合 childNodes 和自定义过滤逻辑实现更精确的操作。