82

I am trying to loop through all the elements retruned from getElementsByTagName("input") using forEach. Any ideas why this does not work in FF, Chrome or IE?

<html>
    <head>
    </head>
    <body>
        <input type="text" value="" />
        <input type="text" value="" />
        <script>
            function ShowResults(value, index, ar) {
                alert(index);
            }
            var input = document.getElementsByTagName("input");
            alert(input.length);
            input.forEach(ShowResults);
    </script>
    </body>
</html>
4

10 回答 10

103

您需要使用以下方法将节点列表转换为数组:

<html>
    <head>
    </head>
    <body>
        <input type="text" value="" />
        <input type="text" value="" />
        <script>
            function ShowResults(value, index, ar) {
                alert(index);
            }
            var input = document.getElementsByTagName("input");
            var inputList = Array.prototype.slice.call(input);
            alert(inputList.length);
            inputList.forEach(ShowResults);
    </script>
    </body>
</html>

或使用 for 循环。

for(let i = 0;i < input.length; i++)
{
    ShowResults(input[i].value);
}

并将 ShowResults 函数更改为:

function ShowResults(value) {
   alert(value);
}

为什么我们需要这样做?
JavaScript 中的一些对象看起来像一个数组,但它们不是一个。这通常意味着它们具有索引访问和长度属性,但没有数组方法。示例包括特殊变量参数、DOM 节点列表和字符串。类数组对象和通用方法提供了使用类数组对象的技巧。 资源

2019 年 7 月 10 日更新,
现在您可以使用 ES6 [...inputList].forEach,或者Array.from(inputList)

于 2013-10-11T18:17:49.980 回答
64

是的,ES6:

const children = [...parent.getElementsByTagName('tag')];
children.forEach((child) => { /* Do something; */ });

扩展运算符的 MDN 文档 ( ...)

于 2016-12-17T04:59:58.997 回答
21

getElementsByTagName returns an HTMLCollection, which do not have a forEach method. But, there's a simple tweak that will allow you to iterate with forEach without creating an intermediate array: use querySelectorAll instead. querySelectorAll returns a NodeList, and modern browsers have a NodeList.prototype.forEach method:

document.querySelectorAll('input')
  .forEach((input) => {
    console.log(input.value);
  });
<input type="text" value="foo">
<input type="text" value="bar">

Another benefit to using querySelectorAll is that it accepts comma-separated CSS selectors, which are far more flexible and precise than just tag names. For example, the selector

.container1 > span, .container2 > span

will only match spans which are children of elements with a class of container1 or container2:

document.querySelectorAll('.container1 > span, .container2 > span')
  .forEach((span) => {
    span.classList.add('highlight');
  });
.highlight {
  background-color: yellow;
}
<div class="container1">
  <span>foo</span>
  <span>bar</span>
</div>
<div class="container2">
  <span>baz</span>
</div>
<div class="container3">
  <span>buzz</span>
</div>

If you want to use NodeList.prototype.forEach on ancient browsers that do not have the method built-in, simply add a polyfill. The following snippet will work on IE11:

// Polyfill:
if (window.NodeList && !NodeList.prototype.forEach) {
  NodeList.prototype.forEach = function(callback, thisArg) {
    thisArg = thisArg || window;
    for (var i = 0; i < this.length; i++) {
      callback.call(thisArg, this[i], i, this);
    }
  };
}

// Main code:
document.querySelectorAll('.container1 > span, .container2 > span')
  .forEach(function(span) {
    span.classList.add('highlight');
  });
.highlight {
  background-color: yellow;
}
<div class="container1">
  <span>foo</span>
  <span>bar</span>
</div>
<div class="container2">
  <span>baz</span>
</div>
<div class="container3">
  <span>buzz</span>
</div>

于 2019-01-15T01:14:39.630 回答
10

因为input不是数组,所以HTMLCollection 使用for循环会更好。

由于HTMLCollections 是类似数组的对象,因此您可以像这样使用它call Array#forEach

Array.prototype.forEach.call(input, ShowResults);
于 2013-10-11T18:24:24.220 回答
6

原因,这不起作用是因为' getElementsByTagName '返回一个数组 - 像 Object 而不是实际的数组。如果您不知道,这就是它们的外观:-

var realArray = ['a', 'b', 'c'];
var arrayLike = {
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
};

因此,由于类数组对象继承自' Object.prototype '而不是' Array.prototype ',这意味着类数组对象不能访问常见的数组原型方法,如forEach()、push()、map()、过滤器()和切片()。

希望有帮助!

于 2017-07-08T11:36:54.573 回答
5

这是因为input是html集合。html 集合没有 forEach。

您可以通过 Array.prototype.slice 轻松地将其转换为数组

例子:

function ShowResults(value, index, ar) {
            alert(index);
        }
        var input = document.getElementsByTagName("input");
        alert(input.length);
input = Array.prototype.slice.call(input)
        input.forEach(ShowResults);

http://jsfiddle.net/fPuKt/1/

于 2013-10-11T18:20:20.687 回答
3

HTMLCollections 没有与数组相同的方法。您可以通过在浏览器的 javascript 控制台中提示此内容来检查此内容。

var elements = document.getElementsByClassName('some-class');
'forEach' in elements;

true如果elements(在这种情况下)有一个方法被调用,控制台将返回forEach

于 2013-10-11T18:28:28.553 回答
3

在 ES6 中,您可以使用spread运算符将​​ HtmlCollection 转换为数组。看到这个问题Why can't I use Array.forEach on a collection of Javascript elements?

input = [...input]
input.forEach(ShowResults)
于 2016-09-30T18:45:07.443 回答
3

如果你可以使用 ES2015,你可以使用Array.from()HTMLCollection返回的 bygetElementsByTagName()转换成一个真正的数组。如果将第 11 行更改为以下内容,则其余代码将按原样工作:

var input = Array.from(document.getElementsByTagName("input"));
于 2019-09-26T15:43:48.950 回答
2

我这样做了:

HTMLCollection.prototype.map = Array.prototype.map;

您现在可以在每个HTMLCollection.

document.getElementsByTagName("input").map(
    input => console.log(input)
);
于 2018-01-07T00:46:54.557 回答