JS:使用Array.forEach遍历getElementsByClassName的结果


240

我想遍历一些DOM元素,我正在这样做:

document.getElementsByClassName( "myclass" ).forEach( function(element, index, array) {
  //do stuff
});

但我得到一个错误:

document.getElementsByClassName(“ myclass”)。forEach不是函数

我使用的是Firefox 3,所以我知道这两个getElementsByClassNameArray.forEach都存在。这工作正常:

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

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

Answers:


384

不需要。如DOM4中指定,它是一个HTMLCollection(至少在现代浏览器中。较旧的浏览器返回NodeList)。

在所有现代浏览器(几乎所有其他IE <= 8)中,您都可以调用Array的forEach方法,并将其元素列表(为HTMLCollectionNodeList)作为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) {...});

如果您能够使用ES6感到很满意(例如,您可以放心地忽略Internet Explorer或使用ES5编译器),则可以使用Array.from

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

29
无需先将其转换为数组。只需使用[].forEach.call(elsArray, function () {...})
凯-SE是邪恶的

1
它不是NodeList。这是一个类似数组的对象。我什至不认为它具有实例类型。querySelectorAll方法虽然返回一个NodeList。
马克西姆六世。

2
@MaksimVi。您是完全正确的:DOM4指定document.getElementsByClassName()应返回一个HTMLCollection(非常相似,但不是NodeList)。感谢您指出错误。
蒂姆·唐

@MaksimVi .:我想知道这是否在某个时候改变了。我通常检查这些东西。
蒂姆·唐

1
@TimDown,谢谢HTMLCollection提示。现在,我终于可以HTMLCollection.prototype.forEach = Array.prototype.forEach;在我的代码中使用了。
马克西姆六世。

70

您可以Array.from用来将集合转换为数组,这比Array.prototype.forEach.call以下方法更干净:

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

在不支持的旧版浏览器中Array.from,您需要使用Babel之类的工具。


ES6还添加了以下语法:

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

休息解构...所有阵列状物体,不仅排列自己的作品,那就好旧数组语法用于构建从值的数组。


尽管替代函数querySelectorAll(有点getElementsByClassName过时)返回了一个forEach本机具有的集合,但其他方法(例如map或)filter丢失了,因此此语法仍然有用:

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

[...document.querySelectorAll(".myclass")].map(element => element.innerHTML);

6
注意:如未建议进行转码(Babel),则在IE <Edge,Opera,Safari <9,Android浏览器,Chrome for Android等方面不兼容。来源:mozilla dev docs
Sean

30

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

document.querySelectorAll('.myclass').forEach(...)

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

MDN: Document.querySelectorAll()


4
请记住,对getElementByClassName的性能损失
SzabolcsPáll18年

3
与其他更密集的任务(如修改DOM)相比,性能损失可忽略不计。如果我在1毫秒内执行其中的60,000个,我很确定这对于任何合理的使用都不会是一个问题:)
icl7126 '19

1
您链接了错误的基准。这是正确的一种度量,即.net / Benchmarks / Show / 4076/0 /…只需在我的低端手机上运行它,即可获得160k / s和380k / s的对比。既然您提到了DOM操作,这也是measurethat.net/Benchmarks/Show/5705/0/…的结果: 50k / s与130k / s。如您所见,操纵DOM甚至更慢,这可能是由于NodeList是静态的(如其他人所提到的)。在大多数使用情况下仍然可以忽略不计,但是速度却慢了将近3倍。
SzabolcsPáll19年

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>

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


9

的结果getElementsByClassName()不是Array,而是类似数组的object。具体来说,它被称为an HTMLCollection,不要与它混淆NodeList它有自己的forEach()方法)。

ES2015转换Array.prototype.forEach()尚未使用的类似数组的对象的一种简单方法尚未提及,即使用散布运算符或散布语法

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

[...elementsArray].forEach((element, index, array) => {
    // do something
});

2
我觉得这确实是在现代浏览器中执行此操作的正确方法。这是创建要解决的确切用例扩展语法。
马特·科罗斯托夫


3

如前所述,getElementsByClassName返回HTMLCollection,其定义为

[Exposed=Window]
interface HTMLCollection {
  readonly attribute unsigned long length;
  getter Element? item(unsigned long index);
  getter Element? namedItem(DOMString name);
};

以前,某些浏览器改为返回NodeList

[Exposed=Window]
interface NodeList {
  getter Node? item(unsigned long index);
  readonly attribute unsigned long length;
  iterable<Node>;
};

区别很重要,因为DOM4现在将NodeList定义为可迭代的。

根据Web IDL草案,

实现声明为可迭代支持的接口的对象将被迭代以获得值序列。

注意:在ECMAScript语言绑定中,可迭代的接口在其接口原型对象上将具有“条目”,“ forEach”,“键”,“值”和 @@ iterator属性。

这意味着,如果要使用forEach,可以使用DOM方法,该方法返回NodeList,例如querySelectorAll

document.querySelectorAll(".myclass").forEach(function(element, index, array) {
  // do stuff
});

请注意,这尚未得到广泛支持。另请参阅Node.childNodes的forEach方法?


1
铬49回forEach in not a function
维塔利Zdanevich

@VitalyZdanevich尝试使用Chromium 50
Oriol

在Chrome 50上,我得到document.querySelectorAll(...).forEach is not a function
Vitaly Zdanevich 2016年

@VitalyZdanevich它的工作铬50,仍然工作在铬53.也许它不被认为足够稳定运到Chrome浏览器50
奥里奥尔


1

这是更安全的方法:

var elements = document.getElementsByClassName("myclass");
for (var i = 0; i < elements.length; i++) myFunction(elements[i]);

0

getElementsByClassName在现代浏览器中返回HTMLCollection

这是 类似于数组的对象,类似于可以通过for...of循环迭代的参数,请参见下面的MDN文档所说的内容:

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

for (let element of getElementsByClassName("classname")){
   element.style.display="none";
}

根据打字稿,事实并非如此:error TS2488: Type 'HTMLCollectionOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.
乌龟很可爱

@TurtlesAreCute,这里OP使用的是JavaScript而不是打字稿,我已经按照香草js的建议进行了回答,因此在打字稿中,它可以是该问题的不同解决方案。
Haritsinh Gohil

@TurtlesAreCute,顺便说一句,它也可以在typescript中工作,但是您必须提到正确的变量类型,该变量包含特定css类的元素,因此可以对其进行相应的转换,有关详细信息,请参见此答案
Haritsinh Gohil

0

这是我在jsperf上创建的测试:https ://jsperf.com/vanillajs-loop-through-elements-of-class

Chrome和Firefox中性能最好的版本是结合使用document.getElementsByClassName的旧版for循环:

var elements = document.getElementsByClassName('testClass'), elLength = elements.length;
for (var i = 0; i < elLength; i++) {
    elements.item(i).textContent = 'Tested';
};

在Safari中,这种变体是赢家:

var elements = document.querySelectorAll('.testClass');
elements.forEach((element) => {
    element.textContent = 'Tested';
});

如果您想为所有浏览器使用最出色的版本,可能就是这样:

var elements = document.getElementsByClassName('testClass');
Array.from(elements).map(
    (element) => {
        return element.textContent = 'Tested';
    }
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.