263

我想知道,JavaScript 提供了多种方法来从任何元素中获取第一个子元素,但哪种方法最好?最好,我的意思是:在行为方面,大多数跨浏览器兼容、最快、最全面和可预测。我用作别名的方法/属性列表:

var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]

这适用于两种情况:

var child = elem.childNodes[0]; // or childNodes[1], see below

这是在形式或<div>迭代的情况下。如果我可能遇到文本元素:

var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;

据我所知,firstChild使用来自 的 NodeListchildNodesfirstElementChild使用children. 我将这个假设基于 MDN 参考:

childNode是对元素节点的第一个子元素的引用,或者null如果没有。

我猜测,就速度而言,差异(如果有的话)将几乎没有,因为firstElementChild实际上是对 的引用children[0],并且children对象已经在内存中。

什么让我失望,是childNodes对象。我用它来查看表格元素中的表格。虽然children列出了所有表单元素,但childNodes似乎也包括 HTML 代码中的空格:

console.log(elem.childNodes[0]);
console.log(elem.firstChild);

两个日志<TextNode textContent="\n ">

console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);

所有日志<input type="text"……<代码>>。怎么会?我知道一个对象将允许我使用“原始”HTML 代码,而另一个坚持使用 DOM,但该childNodes元素似乎在两个级别上都有效。

回到我最初的问题,我的猜测是:如果我想要最全面的对象,childNodes就是要走的路,但由于它的全面性,就返回我想要的元素而言,它可能不是最可预测的/期待在任何给定的时刻。在这种情况下,跨浏览器支持也可能是一个挑战,尽管我可能是错的。

谁能澄清手头对象之间的区别?如果有速度差异,无论多么微不足道,我也想知道。如果我看到这一切都错了,请随时教育我。


PS:拜托,拜托,我喜欢 JavaScript,所以是的,我想处理这种事情。像“jQuery 为你处理这个”之类的答案不是我想要的,因此没有标记。

4

5 回答 5

243

听起来你想多了。您已经观察到 和 之间的区别childNodeschildrenchildNodes包含所有节点,包括完全由空格组成的文本节点,而children只是作为元素的子节点的集合。这就是它的全部。

尽管有几个问题需要注意,但这两个集合都没有什么不可预测的:

  • IE <= 8 不包含纯空格文本节点,childNodes而其他浏览器包含
  • IE <= 8 包含注释节点,children而其他浏览器只有元素

children,firstElementChild和朋友只是方便,呈现仅限于元素的 DOM 过滤视图。

于 2012-04-30T09:52:45.153 回答
24

firstElementChild 在 IE<9 中可能不可用(仅 firstChild)

在 IE<9 firstChild 是 firstElementChild 因为 MS DOM (IE<9) 不存储空文本节点。但如果你在其他浏览器上这样做,它们将返回空文本节点......

我的解决方案

child=(elem.firstElementChild||elem.firstChild)

即使在 IE<9 上,这也会给第一个孩子

于 2012-04-30T09:33:39.940 回答
23

跨浏览器的方法是使用childNodesget ,然后使用ELEMENT_NODENodeList创建一个包含所有节点的数组。nodeType

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    var childNodes = element.childNodes,
        children = [],
        i = childNodes.length;

    while (i--) {
        if (childNodes[i].nodeType == 1) {
            children.unshift(childNodes[i]);
        }
    }

    return children;
}

http://jsfiddle.net/s4kxnahu/

如果您使用诸如lodash 之类的实用程序库,这尤其容易:

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    return _.where(element.childNodes, {nodeType: 1});
}

未来:

您可以与伪类querySelectorAll结合使用(匹配作为选择器参考点的元素)::scope

parentElement.querySelectorAll(':scope > *');

在撰写本文时,Chrome、Firefox 和 Safari:scope都支持此功能。

于 2014-11-24T10:18:54.570 回答
5

只是为了添加其他答案,这里仍然存在值得注意的差异,特别是在处理<svg>元素时。

我已经使用了这两种方法.childNodes.children并且更喜欢使用getterHTMLCollection提供的方法。.children

然而今天,我.children<svg>. 虽然.childrenIE 支持基本 HTML 元素,但不支持文档/文档片段SVG 元素

对我来说,我能够通过简单地抓取所需的元素,.childNodes[n]因为我不需要担心多余的文本节点。你也许可以这样做,但正如上面其他地方所提到的,不要忘记你可能会遇到意想不到的元素。

希望这对挠头试图弄清楚为什么.children在现代 IE 上的 js 中的其他地方有效而在文档或 SVG 元素上失败的人有所帮助。

于 2016-06-21T17:44:57.730 回答
3

不要让空白欺骗你。只需在控制台浏览器中测试即可。

使用本机 JavaScript。这是两个具有相同类的“ul”集的示例。您不需要将“ul”列表全部放在一行中以避免出现空白,只需使用数组计数跳过空白即可。

querySelector()如何使用childNodes[]js fiddle 链接绕过空白: https ://jsfiddle.net/aparadise/56njekdo/

var y = document.querySelector('.list');
var myNode = y.childNodes[11].style.backgroundColor='red';

<ul class="list">
    <li>8</li>
    <li>9</li>
    <li>100</li>
</ul>

<ul class="list">
    <li>ABC</li>
    <li>DEF</li>
    <li>XYZ</li>
</ul>
于 2016-03-27T06:06:00.527 回答