JS: iterando sobre o resultado de getElementsByClassName usando Array.forEach

Eu quero iterar sobre alguns elementos DOM, estou fazendo isso:

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

mas recebo um erro: document.getElementsByClassName (“myclass”). forEach não é uma function

Eu estou usando o Firefox 3, então eu sei que getElementsByClassName e Array.forEach estão presentes. Isso funciona bem:

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

O resultado de getElementsByClassName uma matriz? se não, o que é?

Não. Conforme especificado no DOM4 , é uma HTMLCollection (em navegadores modernos, pelo menos. Navegadores antigos retornavam um NodeList ).

Em todos os navegadores modernos (praticamente qualquer outro IE <= 8), você pode chamar o método forEach de Array, passando-lhe a lista de elementos (seja HTMLCollection ou NodeList ) como this valor:

 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) {...}); 

Você pode usar o Array.from para converter a coleção em array, que é muito mais limpo do que o Array.prototype.forEach.call :

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

Em navegadores mais antigos que não suportam o Array.from , você precisa usar algo como o Babel.


O ES6 também adiciona essa syntax:

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

Rest desestruturar com ... funciona em todos os objects parecidos com matrizes, não apenas matrizes em si, então uma boa syntax de matriz antiga é usada para construir uma matriz a partir dos valores.


Enquanto a function alternativa querySelectorAll (que meio que torna getElementsByClassName obsoleta) retorna uma coleção que tem forEach nativamente, outros methods como map ou filter estão faltando, então esta syntax ainda é útil:

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

Edit: Embora o tipo de retorno tenha mudado em novas versões do HTML (veja a resposta atualizada do Tim Down), o código abaixo ainda funciona.

Como outros disseram, é um NodeList. Aqui está um exemplo completo e funcional que você pode experimentar:

        

getElementsByClassName Test

This is an odd para.

This is an even para.

This one is also odd.

This one is not odd.

Isso funciona no IE 9, FF 5, Safari 5 e Chrome 12 no Win 7.

Ou você pode usar querySelectorAll que retorna NodeList :

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

Suportado por navegadores modernos (incluindo o Edge, mas não o IE):
Posso usar o querySelectorAll?
NodeList.prototype.forEach ()

MDN: Document.querySelectorAll ()

O resultado de getElementsByClassName é uma matriz?

Não

se não, o que é?

Como com todos os methods DOM que retornam múltiplos elementos, é um NodeList, veja https://developer.mozilla.org/en/DOM/document.getElementsByClassName

O resultado de getElementsByClassName() não é uma matriz, mas um object semelhante a uma matriz . Especificamente, ele é chamado de HTMLCollection e não deve ser confundido com o NodeList ( que possui seu próprio método forEach() ).

Uma maneira simples com o ES2015 para converter um object do tipo array para uso com o Array.prototype.forEach() que ainda não foi mencionado é usar o operador spread ou a syntax de propagação :

 const elementsArray = document.getElementsByClassName('myclass'); [...elementsArray].forEach((element, index, array) => { // do something }); 

Como já dito, getElementsByClassName retorna um HTMLCollection , que é definido como

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

Anteriormente, alguns navegadores retornavam um NodeList .

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

A diferença é importante, porque o DOM4 agora define o NodeList como iterável.

De acordo com o rascunho da Web IDL ,

Objetos implementando uma interface que é declarada como suporte iterável sendo iterada para obter uma seqüência de valores.

Nota : Na binding de idioma do ECMAScript, uma interface iterável terá “inputs”, “para cada”, “chaves”, “valores” e propriedades do @@ iterator em seu object de protótipo de interface .

Isso significa que, se você quiser usar forEach , você pode usar um método DOM que retorna um NodeList , como querySelectorAll .

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

Observe que isso ainda não é amplamente suportado. Veja também o método forEach of Node.childNodes?

Não retorna um Array , ele retorna um NodeList .