【问题标题】: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]);
}

按预期工作。

为什么?

【问题讨论】:

    标签: javascript for-loop foreach parent-child


    【解决方案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,而不是NodeListNodeList 现在确实有 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 =&gt; 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 方法结合使用,则会造成困难。
        • 尽管如此,知道这是可能的还是很有趣的。
        • 不要这样做。如果您必须扩展原生原型,请首先检查它们的存在,然后将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);
          });
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-09-20
            • 2023-04-11
            • 1970-01-01
            • 1970-01-01
            • 2020-08-23
            • 2021-03-04
            • 1970-01-01
            • 2013-02-01
            相关资源
            最近更新 更多