获取子节点的最佳方法

我想知道,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的节点列表,而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为你处理这个”这样的答案不是我要找的,因此没有标签。

564451 次浏览

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

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

我的解决方案

< p > <代码> 孩子= (elem.firstElementChild | | elem.firstChild) < /代码> < / p >

这将使第一个孩子在IE<9

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

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

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

childrenfirstElementChild和friends只是方便,呈现了DOM的过滤视图,仅限于元素。

跨浏览器的方法是使用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 > *');

在写这支持:scope在Chrome, Firefox和Safari。

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

使用本地javascript。这里有一个例子,两个'ul'集具有相同的类。你不需要让你的'ul'列表都在一行中,以避免空白,只需使用你的数组计数跳过空白。

如何绕过空白与querySelector()然后childNodes[] js小提琴link: 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>

只是补充一下其他答案,这里仍然有值得注意的差异,特别是在处理<svg>元素时。

我使用过.childNodes.children,并且更喜欢使用由.children getter传递的HTMLCollection

然而,今天我在<svg>上使用.children时遇到了IE/Edge失败的问题。 虽然IE在基本HTML元素上支持.children,但它不支持文档/文档片段, SVG元素. c。< / p >

对我来说,我能够简单地通过.childNodes[n]获取所需的元素,因为我不需要担心无关的文本节点。您也可以做到这一点,但是正如上面所提到的,不要忘记您可能会遇到意想不到的因素。

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