获取子节点的最佳方法

时间:2012-04-30 09:25:36

标签: javascript dom elements children

我想知道,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使用childNodes中的NodeList,firstElementChild使用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为你处理这个问题”这样的答案并不是我想要的,因此没有标签。

5 个答案:

答案 0 :(得分:198)

听起来你正在过度思考它。您已观察到childNodeschildren之间的区别,即childNodes包含所有节点,包括完全由空格组成的文本节点,而children是仅包含空格的集合作为元素的子节点。这就是它的全部内容。

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

  • IE&lt; = 8不包括childNodes中仅限空格的文本节点,而其他浏览器
  • IE&lt; = 8包括children内的评论节点,而其他浏览器仅包含元素

childrenfirstElementChild和朋友只是方便,展示了仅限于元素的DOM的过滤视图。

答案 1 :(得分:23)

firstElementChild可能在IE&lt; 9(仅firstChild)

中不可用

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

我的解决方案

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

这将使第一个孩子甚至在IE&lt; 9

答案 2 :(得分:16)

跨浏览器的方法是使用childNodes获取NodeList,然后使用nodeType ELEMENT_NODE制作所有节点的数组。

/**
 * 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 is supported时。

答案 3 :(得分:2)

嘿,伙计们不要让白色空间欺骗你。只需在控制台浏览器中测试它。 使用原生javascript。以下是具有相同类的两个'ul'集的示例。您不需要将'ul'列表全部放在一行中以避免空格只使用数组计数来跳过空白区域。

如何使用querySelector()然后childNodes[] js小提琴链接绕过空格: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>

答案 4 :(得分:2)

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

我已使用.childNodes.children,并希望使用HTMLCollection获取者提供的.children

然而今天,在.children上使用<svg>时,我遇到了IE / Edge失败的问题。 IE中基本HTML元素it isn't supported on document/document fragments, or SVG elements支持.children

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

希望这有助于有人试图弄清楚为什么.children在现代IE上的js中的其他地方工作并且在文档或SVG元素上失败。