JS:使用Array.forEach迭代getElementsByClassName的结果

Ste*_*dge 205 javascript foreach getelementsbyclassname

我想迭代一些DOM元素,我这样做:

document.getElementsByClassName( "myclass" ).forEach( function(element, index, array) {
  //do stuff
});
Run Code Online (Sandbox Code Playgroud)

但我得到一个错误:document.getElementsByClassName("myclass").forEach不是一个函数

我使用的是Firefox 3,所以我知道这两个getElementsByClassNameArray.forEach都存在.这很好用:

[2, 5, 9].forEach( function(element, index, array) {
  //do stuff
});
Run Code Online (Sandbox Code Playgroud)

getElementsByClassName数组的结果?如果没有,那是什么?

Tim*_*own 333

没有.正如在DOM4中指出的那样,它是一个HTMLCollection(在现代浏览器中,至少.旧的浏览器返回了一个NodeList).

在所有现代浏览器中(几乎所有其他IE <= 8),您可以调用Array的forEach方法,将元素列表(是HTMLCollection或者NodeList)作为this值传递给它:

var els = document.getElementsByClassName("myclass");

Array.prototype.forEach.call(els, function(el) {
    // Do stuff here
    console.log(el.tagName);
});

// Or
[].forEach.call(els, function (el) {...});
Run Code Online (Sandbox Code Playgroud)

如果您处于能够使用ES6的幸福位置(即您可以安全地忽略Internet Explorer或者您正在使用ES5转换器),您可以使用Array.from:

Array.from(els).forEach((el) => {
    // Do stuff here
    console.log(el.tagName);
});
Run Code Online (Sandbox Code Playgroud)

  • 无需先将其转换为数组.只需使用`[].forEach.call(elsArray,function(){...})`. (27认同)
  • 它不是节点列表。它是一个类似数组的对象。我什至不认为它有实例类型。不过,“querySelectorAll”方法返回一个 NodeList。 (3认同)
  • @马克西姆维。你说得对:DOM4 指定`document.getElementsByClassName()` 应该返回一个 [`HTMLCollection`](http://www.w3.org/TR/dom/#htmlcollection)(这非常相似,但不是节点列表)。感谢您指出错误。 (2认同)
  • @TimDown,感谢“HTMLCollection”提示。现在我终于可以在代码中使用“HTMLCollection.prototype.forEach = Array.prototype.forEach;”了。 (2认同)

Ath*_*ari 63

您可以使用Array.from将集合转换为数组,这比Array.prototype.forEach.call以下更清晰:

Array.from(document.getElementsByClassName("myclass")).forEach(
    function(element, index, array) {
        // do stuff
    }
);
Run Code Online (Sandbox Code Playgroud)

在不支持的旧浏览器中Array.from,您需要使用像Babel这样的东西.


ES6还添加了以下语法:

[...document.getElementsByClassName("myclass")].forEach(
    (element, index, array) => {
        // do stuff
    }
);
Run Code Online (Sandbox Code Playgroud)

使用...所有类似数组的对象进行休息解构,而不仅仅是数组本身,然后使用良好的旧数组语法从值构造数组.


虽然替代功能querySelectorAll(这有点让getElementsByClassName过时)返回集合其中确实有forEach原生,其他的方法,如mapfilter丢失,所以这句法仍然是有用的:

[...document.querySelectorAll(".myclass")].map(
    (element, index, array) => {
        // do stuff
    }
);

[...document.querySelectorAll(".myclass")].map(element => element.innerHTML);
Run Code Online (Sandbox Code Playgroud)

  • 注意:没有按照建议(Babel)进行传播,这在IE <Edge,Opera,Safari <9,Android浏览器,Chrome for Android等等中不兼容[来源:mozilla dev docs](https://开发人员.mozilla.org/EN /文档/网络/的JavaScript /参考/ Global_Objects /阵列/从) (5认同)

icl*_*126 20

或者你可以使用querySelectorAll哪个返回NodeList:

document.querySelectorAll('.myclass').forEach(...)
Run Code Online (Sandbox Code Playgroud)

现代浏览器支持(包括Edge,但不支持IE):
我可以使用querySelectorAll
NodeList.prototype.forEach()

MDN: Document.querySelectorAll()

  • 您链接了错误的基准。这是正确的 https://www.measurethat.net/Benchmarks/Show/4076/0/queryselectorall-vs-getelementsbyclassname 刚刚在我的低端手机上运行它,得到 160k/s 与 380k/s。既然你提到了 DOM 操作,这里也是 https://www.measurethat.net/Benchmarks/Show/5705/0/queryselectorall-vs-getelementsbyclassname-with-foreach 得到了 50k/s 与 130k/s。正如您所看到的,操作 DOM 甚至更慢,可能是由于 NodeList 是静态的(正如其他人提到的)。在大多数用例中仍然可以忽略不计,但速度却慢了近 3 倍。 (5认同)
  • 注意getElementByClassName的性能损失 (3认同)
  • 与其他更密集的任务(例如修改DOM)相比,**性能损失可以忽略不计**。如果我在1毫秒内执行[其中60,000个](https://jsperf.com/getelementbyid-vs-queryselector-vs-queryselector-by-id),我很确定这不会成为任何合理的问题用法:) (2认同)

jam*_*iss 14

编辑:虽然新版本的HTML中的返回类型已更改(请参阅Tim Down的更新答案),但以下代码仍然有效.

正如其他人所说,它是一个NodeList.这是一个完整的,可以尝试的工作示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <script>
            function findTheOddOnes()
            {
                var theOddOnes = document.getElementsByClassName("odd");
                for(var i=0; i<theOddOnes.length; i++)
                {
                    alert(theOddOnes[i].innerHTML);
                }
            }
        </script>
    </head>
    <body>
        <h1>getElementsByClassName Test</h1>
        <p class="odd">This is an odd para.</p>
        <p>This is an even para.</p>
        <p class="odd">This one is also odd.</p>
        <p>This one is not odd.</p>
        <form>
            <input type="button" value="Find the odd ones..." onclick="findTheOddOnes()">
        </form>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

这适用于Win 7上的IE 9,FF 5,Safari 5和Chrome 12.


小智 7

结果getElementsByClassName()不是数组,而是类似数组的对象.特别是它被称为HTMLCollection,不要混淆NodeList(它有自己的forEach()方法).

与ES2015一个简单的方法来转换类似阵列的对象与使用Array.prototype.forEach()已尚未提及是使用传播运营商或传播语法:

const elementsArray = document.getElementsByClassName('myclass');

[...elementsArray].forEach((element, index, array) => {
    // do something
});
Run Code Online (Sandbox Code Playgroud)

  • 我觉得这确实是在现代浏览器中执行此操作的正确方法。这是创建要解决的确切用例扩展语法。 (2认同)

小智 6

对于打字稿,我更喜欢迭代

for(let element of Array.from(document.querySelectorAll('.myclass'))){
   //my code
}
Run Code Online (Sandbox Code Playgroud)


Que*_*tin 5

getElementsByClassName的结果是一个数组吗?

没有

如果没有,那是什么?

与返回多个元素的所有DOM方法一样,它是一个NodeList,请参阅https://developer.mozilla.org/en/DOM/document.getElementsByClassName


Har*_*hil 5

getElementsByClassName在现代浏览器中返回HTMLCollection

这是 类似数组的对象,类似于可通过for...of循环迭代的参数,请参见下面的MDN文档对此的描述:

对...的语句创建一个迭代循环迭代上的对象,包括:内置的字符串,数组,数组状物体(如参数 或节点列表),TypedArray,地图,设置,和用户自定义iterables。它调用自定义迭代挂钩,其中包含要为对象的每个不同属性的值执行的语句。

Javascript 示例

for (const element of document.getElementsByClassName("classname")){
   element.style.display="none";
}
Run Code Online (Sandbox Code Playgroud)

打字稿示例

let elements = document.getElementsByClassName('classname');
let i;

for (i = 0; i < elements.length; i++) {

  if (elements[i] instanceof HTMLElement) {
    elements[i].style.display = "none";
  }

}
Run Code Online (Sandbox Code Playgroud)

  • 事实并非如此,根据 Typescript:“错误 TS2488:类型 'HTMLCollectionOf&lt;Element&gt;' 必须具有返回迭代器的 '[Symbol.iterator]()' 方法。” (2认同)