获取子节点的最佳方法


233

我想知道,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使用来自的NodeList childNodes,并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为您处理此问题”之类的答案不是我想要的,因此没有 标签。


49
>>拜托,拜托,我喜欢JavaScript,是的,我想处理这种事情。像“ jQuery为您处理此问题”之类的答案不是我在寻找的<< <<为此投票
david.barkhuizen 2015年

Answers:


211

听起来您想得太多了。你观察到的区别childNodeschildren,这是一个childNodes包含所有节点,包括完全由空白的文本节点,而children只是对元素的子节点的集合。真的就是全部。

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

  • IE <= 8不包含纯空白文本节点,childNodes而其他浏览器则包含
  • IE <= 8内包含注释节点,children而其他浏览器仅包含元素

childrenfirstElementChild和朋友只是便利,呈现仅仅限于元件DOM的过滤视图。


想出了很多,尽管还有一件事让我感到烦恼:childNodes选择的空白textnode实际上只是换行和代码中的几个选项卡。这是由于XHTML doctype吗?封闭空格以在标签内构造代码可以解决此问题?
Elias Van Ootegem

@EliasVanOotegem:这与doctype无关。无论HTML和XHTML出现在源中的何处,空白文本节点始终都包含在DOM中(IE <9中除外)。这通常是一件好事,尽管如果您不为此做好准备,它可能会吸引您。
蒂姆·唐

我的意思是写这样的标记是否有帮助<td\n\t>content</td>。就像处理XML一样,避免多余的空格被视为数据(在这种情况下为DOM)的一部分。为什么标记内的空格会包含在DOM中?
Elias Van Ootegem

@EliasVanOotegem:哦,我知道了。标签内标签名称后面的空白将被忽略,因此您可以执行这种操作。我已经看到了在精确布局中使用的技术,可以防止浏览器在某些类型的元素之间留出很小的空白。
蒂姆·唐

2
@克里斯托夫:啊,很公平,谢谢。我将在答案中添加注释。鉴于它children起源于IE 4已有十多年的时间,后来成为标准或被其他浏览器采用,您可以认为IE <= 8是正确的,而其他浏览器是错误的。
Tim Down

23

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

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

我的解决方案

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

即使在IE <9的情况下,这也能使第一个孩子


如果elem.firstChild不是元素节点,则在旧IE中的结果将有所不同,因为elem.firstElementChild它始终是元素节点。
2012年

抱歉,但是我知道如何在所有主流浏览器中获得第一个孩子。我想知道的是不同对象的结构,以便更好地了解它们之间的异同。后来我将修改我的问题做出更清晰,遗憾的查询股价
埃利亚斯范Ootegem

1
firstElementChild在非IE浏览器中也不一定安全:Firefox仅在版本3.5中开始支持它。
Tim Down

这适用于chrome,IE9和IE8,因为如果它们具有firstElementChild,则不执行其他检查,否则(旧IE)我们具有firstChild,并且它是不具有firstElementChild(IE <9 )...我仍然对FF感到疑惑
neu-rah 2012年

1
Tim是正确的,当我在谷歌上搜索这些对象的东西表明了,好发部位在一段时间检查一次
埃利亚斯凡Ootegem

20

跨浏览器的方法是使用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支持功能


:scope已从规格中删除。我们应该删除“ 未来”部分吗?
Thomas Marti

4

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

我已经使用了.childNodes和,.children并且更喜欢使用getter HTMLCollection传递的内容.children

然而今天,我遇到了与IE /边缘使用时失败的问题.children<svg>。虽然.children在IE被支撑在基本的HTML元素,它不支持文件/文件的片段, SVG元素

对我来说,我可以轻松获取所需的元素,.childNodes[n]因为我没有多余的文本节点可担心。您也许可以做同样的事情,但是正如上面其他地方提到的,请不要忘记您可能会遇到意外的因素。

希望这对尝试找出.children现代IE 为什么在其js的其他地方起作用而在文档或SVG元素上失败的人有所帮助。


3

嘿,伙计们不要让空格欺骗您。只需在控制台浏览器中进行测试即可。使用本地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>

不是我的不赞成票,而是我认为有人不赞成票是因为:a)这个问题已有4年历史,document.querySelector当时没有得到很好的支持。b)问题基本上是问childrenchildNodes 内部之间有什么区别,这是更可靠的,何时选择一个。和c)因为,正如在问题中证实的那样:childNodes 确实包括空白节点,children所以不...
Elias Van Ootegem
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.