L'utilisation de forEach sur un tableau de getElementsByClassName entraîne "TypeError: undefined is not a function"


91

Dans mon JSFiddle , j'essaye simplement d'itérer sur un tableau d'éléments. Le tableau n'est pas vide, comme le prouvent les instructions du journal. Pourtant, l'appel à forEachme donne l' erreur (pas si utile) «Uncaught TypeError: undefinedis not a function».

Je dois faire quelque chose de stupide; Qu'est-ce que je fais mal?

Mon code:

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
arrn'est pas un tableau, mais un HTMLCollection. Il n'a pas les mêmes méthodes qu'un tableau. developer.mozilla.org/en-US/docs/Web/API/… . Voici même un article de SO à ce sujet: stackoverflow.com/questions/13433799/…
Ian

Quelque chose comme ça [1,2,3].forEach(function(v,i,a) { console.log(v); });va. Quelle est la différence entre ceci et le tableau dans mon exemple?
Jer

Vous ne disposez d' un tableau dans votre exemple. Qu'est-ce qui vous fait penser que c'est un tableau?
Ian

3
@Jer: Comme arr instanceof Arraycela entraînera, falseil ne pourra pas utiliser de méthodes prototypes de l' Arrayobjet telles que Array.prototype.forEach () . arrest une HTMLCollection et un tableau comme un objet (mais n'hérite ni n'instancie Array). Par conséquent, votre forboucle standard fonctionnera car elle itère simplement dans l'index de l'objet et n'est pas un prototype de Array.
Nope

1
@ Jer - vous devriez examiner les différences entre les objets intégrés et hôtes. Le premier est conforme à ECMA-262, le dernier seulement autant que l'hôte le souhaite. Le DOM possède de nombreux objets qui permettent d'accéder aux membres par index (document.images, document.forms, form.elements, select.options, etc.), principalement basés sur l' interface NodeList .
RobG

Réponses:


162

C'est parce que document.getElementsByClassNamerenvoie une HTMLCollection , pas un tableau.

Heureusement, c'est un objet "de type tableau" (ce qui explique pourquoi il est journalisé comme s'il s'agissait d'un objet et pourquoi vous pouvez itérer avec une forboucle standard ), vous pouvez donc faire ceci:

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

Avec ES6 (sur les navigateurs modernes ou avec Babel), vous pouvez également utiliser Array.fromqui construit des tableaux à partir d'objets de type tableau:

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

ou répartissez l'objet de type tableau dans un tableau:

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

2
@Jer en argumentsest un. Les objets jQuery en sont un autre. Vous pourriez en fabriquer un vous-même:var a = {"0": "str1", "1": "str2", length: 2}
Ian

1
Nous y revenons avec les anciens navigateurs… le passage d'un objet hôte à une méthode native échouera dans IE 8 et les versions antérieures. Peut-être que personne ne s'en soucie, mais certains le pourraient. ;-) Oh, il ne supporte pas non plus getElementsByClassName , mais querySelectorAll('.myClass')devrait fonctionner. J'attends toujours que les itérateurs soient ajoutés à l'API NodeList. :-(
RobG

2
@Jer: En guise de remarque, si vous avez l'intention de sortir de la boucle pour une raison quelconque, Array.prototype.forEachne vous laissera pas faire. Si vous avez cette exigence plus tard, utilisez la forboucle standard ou si vous souhaitez utiliser l'objet tableau, utilisez Array.prototype.everyou Array.prototype.some(notez cependant que tous / certains ne sont pas pris en charge dans IE8 ou moins)
Non

1
@Ian Vous avez besoin d'une épissure pour que l'objet soit "en forme de tableau". Comparez les logs ici: jsbin.com/sigut/1/edit
Denys Séguret

1
@Ian TBH la définition de "array-like" est très floue et dépend de l'utilisation. Parfois, je n'inclus pas splicedans cette définition, mais quand je veux être plus "en forme de tableau" pour pouvoir l'utiliser map, filteretc., alors je l'inclus. Une itération simple en utilisant forEachn'a pas besoin splice.
Denys Séguret

11

Essayez ceci, cela devrait fonctionner:

<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

dans le cas où vous souhaitez accéder à l'ID de chaque élément d'une classe spécifique, vous pouvez effectuer les opérations suivantes:

    Array.from(document.getElementsByClassName('myClass')).forEach(function(element) {
        console.log(element.id);
    });
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.