为啥 forEach 不适用于儿童?
Posted
技术标签:
【中文标题】为啥 forEach 不适用于儿童?【英文标题】:Why is forEach not working for children?为什么 forEach 不适用于儿童? 【发布时间】:2013-02-12 05:52:06 【问题描述】:我有一个<div>
,里面有一些孩子<div>
。例如
<div id="niceParent">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
我尝试使用forEach
函数循环遍历它们,因为我认为document.getElementById("niceParent").children
是一个数组,因为我可以使用
console.log(document.getElementById("niceParent").children[1]);
console.log(document.getElementById("niceParent").children[2]);
console.log(document.getElementById("niceParent").children[3]);
console.log(document.getElementById("niceParent").children[4]);
所以我尝试了
document.getElementById("niceParent").children.forEach(function(entry)
console.log(entry);
);
这不起作用。我明白了
TypeError: document.getElementById(...).children.forEach is not a function
作为一种解决方法,我还尝试了一个更复杂的for..in
循环:
for (var i in document.getElementById("niceParent").children)
if (document.getElementById("niceParent").children[i].nodeType == 1) console.log(document.getElementById("niceParent").children[i]);
按预期工作。
为什么?
【问题讨论】:
【参考方案1】:因为.children
包含htmlCollection
[MDN],而不是数组。 HTMLCollection
对象是一个 array-like 对象,它公开了一个 .length
属性并具有数字属性,只是 like 数组,但它不继承自 @987654330 @ 因而不是一个数组。
您可以使用Array.prototype.slice
将其转换为数组:
var children = [].slice.call(document.getElementById(...).children);
ECMAScript 6 引入了一个新的 API,用于将迭代器和类似数组的对象转换为真正的数组:Array.from
[MDN]。尽可能使用它,因为它使意图更加清晰。
var children = Array.from(document.getElementById(...).children);
【讨论】:
我愿意接受 Felix Kling 和 lonesomeday 的回答,因为尽管使用了一些别名,但它们是相同的。谢谢你。尤其是指向 MDN 的链接。 在 ECMAScript6 中,可以使用Array.prototype.from()
以更清晰的方式说明转换为数组的意图。
虽然 Array.from 或 slice 在技术上是可行的,但与简单地使用 for 循环遍历原始 NodeList 相比,所涉及的转换过程肯定会大大降低效率吗?
实际上.children 包含HTMLCollection
,而不是NodeList
。 NodeList
现在确实有 forEach 方法,而 HTMLCollection
没有。
借助扩展运算符,您可以使用 ES6 做得更好。在下面查看我的answer :)【参考方案2】:
Element.children
是不是数组。它是一个名为HTMLCollection
的对象。它们没有数组的方法(尽管它们有 length
属性)。
要遍历它,您必须将其转换为数组,您可以使用Array.prototype.slice
:
var children = Array.prototype.slice.call(document.getElementById("niceParent").children);
children.forEach(…);
【讨论】:
【参考方案3】:将HTMLCollection
(如.children
)转换为数组以使用forEach()
(或map()
等)的更简洁、更现代的方法是在数组[]
中使用spread synthax ...
。
var children = [...document.getElementById('x').children];
例如:
[...document.getElementById('x').children].forEach(child => console.log(child))
这是一个 es6 功能。它适用于所有现代浏览器。
[...document.getElementById('niceParent').children].forEach(child => console.log(child.textContent))
<div id="niceParent">
<div>a</div>
<div>b</div>
<div>c</div>
<div>d</div>
</div>
【讨论】:
【参考方案4】:你也可以这样做:
NodeList.prototype.forEach = HTMLCollection.prototype.forEach = Array.prototype.forEach;
在此之后,您可以在您的集合上调用 forEach:
document.getElementById("niceParent").children.forEach(...)
最好和最安全的方法实际上是只在 forEach 不存在的情况下添加它:
if (window.NodeList && !NodeList.prototype.forEach)
NodeList.prototype.forEach = Array.prototype.forEach;
if (window.HTMLCollection && !HTMLCollection.prototype.forEach)
HTMLCollection.prototype.forEach = Array.prototype.forEach;
【讨论】:
但是,如果与for..in
方法结合使用,则会造成困难。
推荐阅读文章:"What’s wrong with extending the DOM".
尽管如此,知道这是可能的还是很有趣的。
不要这样做。如果您必须扩展原生原型,请首先检查它们的存在,然后将add a non-enumerable, non-constructible method 发送到原型。作为参考,NodeList.prototype.forEach
存在于最近的浏览器中。
在我看来,用一个好的 polyfill 扩展 DOM 并不是一个坏习惯。我会推荐这个答案。 @SebastianSimon 您链接的 MDN 页面将此答案列为 Polyfill:“上述行为是有多少浏览器实际实现 NodeList.prototype.forEach”【参考方案5】:
如果您需要使用轻量级 npm 模块的干净方法来解决上述问题,请查看https://www.npmjs.com/package/foreach-array
例如:
import each from 'foreach-array';
const array = ['First Name', 'Last Name', 'Country'];
each(array, (value, index, array) =>
console.log(index + ': ' + value);
);
// Console log output will be:
// 0: First Name
// 1: Last Name
// 2: Country
对于您的场景,它是document.getElementById("niceParent").children
,而不是上面示例中的array
【讨论】:
【参考方案6】:也许这是一个简单的解决方案:
document.getElementById("niceParent").childNodes.forEach(function(entry)
console.log(entry);
);
【讨论】:
以上是关于为啥 forEach 不适用于儿童?的主要内容,如果未能解决你的问题,请参考以下文章
[SwiftUI]:ForEach 不适用于字典数组、带数组的字典
向 div 添加事件监听器适用于 ForEach() 方法,但不适用于 for 循环。有啥不同?