L'utilizzo di forEach su un array da getElementsByClassName risulta in "TypeError: undefined non è una funzione"


91

Nel mio JSFiddle , sto semplicemente cercando di iterare su una serie di elementi. L'array non è vuoto, come dimostrano le istruzioni log. Tuttavia la chiamata a forEachmi dà l' errore (non così utile) "Uncaught TypeError: undefinednon è una funzione".

Devo fare qualcosa di stupido; Che cosa sto facendo di sbagliato?

Il mio codice:

var arr = document.getElementsByClassName('myClass');
console.log(arr);
console.log(arr[0]);
arr.forEach(function(v, i, a) {
  console.log(v);
});
.myClass {
  background-color: #FF0000;
}
<div class="myClass">Hello</div>


8
arrnon è un array, ma un file HTMLCollection. Non ha gli stessi metodi di un array. developer.mozilla.org/en-US/docs/Web/API/… . Ecco anche un post SO: stackoverflow.com/questions/13433799/…
Ian

Qualcosa di simile [1,2,3].forEach(function(v,i,a) { console.log(v); });va bene. Qual è la differenza tra questo e l'array nel mio esempio?
Ger il

Lei non si dispone di una vasta gamma nel tuo esempio. Cosa ti fa pensare che sia un array?
Ian

3
@ Jer: Come arr instanceof Arrayrisulterà, falsenon potrà avvalersi di alcun metodo prototipo Arraydell'oggetto come Array.prototype.forEach () . arrè un HTMLCollection e un oggetto simile a un array (ma non eredita da o istanzia Array). Quindi il tuo forciclo standard funzionerà in quanto itera semplicemente attraverso l'indice dell'oggetto e non è un prototipo di Array.
No

1
@ Jer: dovresti esaminare le differenze tra gli oggetti incorporati e quelli host. Il primo è conforme a ECMA-262, il secondo solo quanto l'ospite desidera. Il DOM ha molti oggetti che consentono l'accesso ai membri da indice (document.images, document.forms, form.elements, select.options, ecc), principalmente basate sulla NodeList interfaccia .
RobG

Risposte:


162

Questo perché document.getElementsByClassNamerestituisce un HTMLCollection , non un array.

Fortunatamente è un oggetto "simile ad un array" (il che spiega perché viene registrato come se fosse un oggetto e perché puoi iterare con un forciclo standard ), quindi puoi farlo:

[].forEach.call(document.getElementsByClassName('myClass'), function(v,i,a) {

Con ES6 (sui browser moderni o con Babel), puoi anche usare Array.fromche costruisce array da oggetti simili a array:

Array.from(document.getElementsByClassName('myClass')).forEach(v=>{

o diffondere l'oggetto simile a un array in un array:

[...document.getElementsByClassName('myClass'))].forEach(v=>{

2
@Jer argumentsè uno. Gli oggetti jQuery sono un altro. Potresti crearne uno tu stesso:var a = {"0": "str1", "1": "str2", length: 2}
Ian

1
Eccoci di nuovo con i vecchi browser ... il passaggio di un oggetto host a un metodo nativo fallirà in IE 8 e versioni precedenti. Forse a nessuno importa, ma qualcuno potrebbe. ;-) Oh, non supporta neanche getElementsByClassName , ma querySelectorAll('.myClass')dovrebbe funzionare. Sto ancora aspettando che gli iteratori vengano aggiunti all'API NodeList. :-(
RobG

2
@ Jer: Come nota a margine, se hai intenzione di uscire dal giro per qualsiasi motivo Array.prototype.forEach, non te lo permetterò. Se si dispone di tale requisito in seguito, utilizzare il forciclo standard o se si desidera utilizzare l'oggetto array, utilizzare Array.prototype.everyo Array.prototype.some(notare che tutti / alcuni non sono supportati in IE8 o meno)
No

1
@Ian Hai bisogno di unire perché l'oggetto sia "simile ad un array". Confronta i registri qui: jsbin.com/sigut/1/edit
Denys Séguret

1
@Ian TBH la definizione di "array-like" è molto confusa e dipende dall'uso. A volte mi non include splicein tale definizione, ma quando voglio essere più "serie-like" per essere in grado di utilizzare map, filtere così via, poi ho includerlo. L'iterazione semplice usando forEachnon ha bisogno splice.
Denys Séguret

11

Prova questo dovrebbe funzionare:

<html>
  <head>
    <style type="text/css">
    </style>
  </head>
  <body>
   <div class="myClass">Hello</div>
   <div class="myClass">Hello</div>

<script type="text/javascript">
    var arr = document.getElementsByClassName('myClass');
    console.log(arr);
    console.log(arr[0]);
    arr = [].slice.call(arr); //I have converted the HTML Collection an array
    arr.forEach(function(v,i,a) {
        console.log(v);
    });
</script>


<style type="text/css">
    .myClass {
    background-color: #FF0000;
}
</style>

  </body>
</html>

0

nel caso in cui desideri accedere all'ID di ogni elemento di una specifica classe puoi fare quanto segue:

    Array.from(document.getElementsByClassName('myClass')).forEach(function(element) {
        console.log(element.id);
    });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.