forEach n'est pas une erreur de fonction avec le tableau JavaScript

145

J'essaye de faire une boucle simple:

const parent = this.el.parentElement
console.log(parent.children)
parent.children.forEach(child => {
  console.log(child)
})

Mais j'obtiens l'erreur suivante:

VM384: 53 Uncaught TypeError: parent.children.forEach n'est pas une fonction

Même si les parent.childrenjournaux:

entrez la description de l'image ici

Quel pourrait être le problème?

Remarque: voici un JSFiddle .

Alexchenco
la source
Le même problème se produit avec element.siblings
Daut
@Daut oui car element.siblings renvoie une HTMLCollection et HTMLCollections n'ont pas la méthode forEach ()
Freddo
1
hé toi, chercheur Google! si vous lisez ceci, vérifiez que c'est forEach avec un E majuscule au lieu de foreach ....
Robert Sinclair

Réponses:

127

Première option: invoquer forEach indirectement

Le parent.childrenest un objet de type Array. Utilisez la solution suivante:

const parent = this.el.parentElement;

Array.prototype.forEach.call(parent.children, child => {
  console.log(child)
});

Le type parent.childrenis NodeList, qui est un objet de type Array car:

  • Il contient la lengthpropriété, qui indique le nombre de nœuds
  • Chaque nœud est une valeur de propriété avec un nom numérique, commençant à 0: {0: NodeObject, 1: NodeObject, length: 2, ...}

Voir plus de détails dans cet article .


Deuxième option: utiliser le protocole itérable

parent.childrenest un HTMLCollection: qui implémente le protocole itérable . Dans un environnement ES2015, vous pouvez utiliser le HTMLCollectionavec n'importe quelle construction qui accepte les itérables.

Utilisation HTMLCollectionavec l'opérateur d'étalement:

const parent = this.el.parentElement;

[...parent.children].forEach(child => {
  console.log(child);
});

Ou avec le for..ofcycle (qui est mon option préférée):

const parent = this.el.parentElement;

for (const child of parent.children) {
  console.log(child);
}
Dmitri Pavlutin
la source
Lorsque j'utilise votre solution, je n'ai plus de problèmes, mais le code à l'intérieur de la fonction anonymisée n'est pas exécuté. .so ..
Jérémy
Quel navigateur utilisez-vous pour que parent.children vous dise qu'il s'agit d'une liste de nœuds. Sur Firefox, il me dit qu'il s'agit d'une HTMLCollection. Si c'était une liste de nœuds, .forEach () fonctionnerait
Freddo
104

parent.childrenn'est pas un tableau. C'est HTMLCollection et il n'a pas de forEachméthode. Vous pouvez d'abord le convertir en tableau. Par exemple dans ES6:

Array.from(parent.children).forEach(child => {
    console.log(child)
});

ou en utilisant l'opérateur d'étalement:

[...parent.children].forEach(function (child) {
    console.log(child)
});
madox2
la source
9
Je préfère cette solution bien plus que de jouer avec le prototype Array.
Daut
Et cette réponse est (l'une des) les bonnes réponses à la question des PO. parent.children est une HTMLCollection qui n'a pas de méthode .forEach
Freddo
18

parent.childrenrenverra une liste de liste de nœuds , techniquement une collection html . C'est un tableau comme un objet, mais pas un tableau, vous ne pouvez donc pas appeler directement des fonctions de tableau dessus. Dans ce contexte, vous pouvez utiliser Array.from()pour convertir cela en un véritable tableau,

Array.from(parent.children).forEach(child => {
  console.log(child)
})
Rajaprabhu Aravindasamy
la source
Non, parent.children ne renvoie pas de nodeList mais une collection HTML. Pas la même chose. Si c'était une liste de nœuds, .forEach fonctionnerait
Freddo
12

Une version plus naïve , au moins vous êtes sûr que cela fonctionnera sur tous les appareils, sans conversion et ES6:

const children = parent.children;
for (var i = 0; i < children.length; i++){
    console.log(children[i]);
}

https://jsfiddle.net/swb12kqn/5/

Jean
la source
2
Voté parce que toutes ces nouvelles fonctions ES6 font exactement la même bonne vieille chose qui était disponible, mais de manière désordonnée, comme toujours avec JS
Freddo
8

parent.childrenest un HTMLCollectionobjet de type tableau. Tout d'abord, vous devez le convertir en réel Arraypour utiliser des Array.prototypeméthodes.

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
  console.log(child)
})
Dmitriy
la source
2
Ou ne le convertissez pas, mais utilisez .call () sur .forEach ()?
nnnnnn
@nnnnnn Voir ma réponse ci-dessous.
Dmitri Pavlutin
Il existe de nombreuses façons de convertir un objet de type tableau en un tableau :) C'est l'un d'entre eux
Dmitriy
@DmitriyLoskutov Vous n'avez pas besoin de le convertir - JavaScript est un langage de frappe canard. Utilisez simplement cette fonctionnalité.
Dmitri Pavlutin
5

C'est parce qu'il parent.childrens'agit d'un NodeList , et qu'il ne prend pas en charge la .forEachméthode (car NodeList est un tableau comme une structure mais pas un tableau), alors essayez de l'appeler en le convertissant d'abord en tableau en utilisant

var children = [].slice.call(parent.children);
children.forEach(yourFunc);
Ammar Hasan
la source
Non, ce n'est pas une NodeList, c'est une collection HTML
Freddo
5

Il n'y a pas besoin de l 'forEach , vous pouvez itérer en utilisant uniquement le fromdeuxième paramètre de, comme ceci:

let nodeList = [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]
Array.from(nodeList, child => {
  console.log(child)
});

Armfoot
la source
La triste nouvelle est que parent.children n'est pas une nodeList ... .from () ne fonctionnera pas.
Freddo
@Cedric si votre objet n'est pas une NodeList, alors vous devriez poser une nouvelle question spécifiquement pour y répondre. Ici, le vote défavorable est utilisé lorsque la réponse est intrinsèquement fausse ou nuisible et, comme vous pouvez le voir par l'extrait de code, tous les éléments de l'objet sont itérés et imprimés, ce qui était le but de la question du PO.
Armfoot
Oui, le problème est que la question de l'OP était liée à une collection HTML, pas à une nodeList ... Donc la réponse n'était tout simplement pas de répondre à la question
Freddo
@Cedric cette réponse itérera également sur une collection HTML car Array.fromconvertit l'objet donné dans le premier paramètre en un tableau. Le résultat est le même que dans la réponse de madox2 sans avoir besoin d'une forEachboucle supplémentaire ( documentation Array.fromMDN ).
Armfoot
4

Si vous essayez de boucler sur un NodeListcomme ceci:

const allParagraphs = document.querySelectorAll("p");

Je recommande fortement de le boucler de cette façon:

Array.prototype.forEach.call(allParagraphs , function(el) {
    // Write your code here
})

Personnellement, j'ai essayé plusieurs méthodes, mais la plupart d'entre elles n'ont pas fonctionné car je voulais en boucle sur un NodeList, mais celui-ci fonctionne comme un charme, essayez-le!

Ce NodeListn'est pas un tableau, mais nous le traitons comme un tableau, en utilisant Array.Donc, vous devez savoir qu'il n'est pas pris en charge dans les anciens navigateurs!

Besoin de plus d'informations sur NodeList? Veuillez lire sa documentation sur MDN .

Elharony
la source
1
Cette réponse fonctionne évidemment sur nodeList. Le problème est que parent.children renvoie une collection HTML, qui n'est pas une nodeList ...
Freddo
3

Puisque vous utilisez des fonctionnalités d'ES6 ( fonctions fléchées ), vous pouvez également simplement utiliser une boucle for comme celle-ci:

for(let child of [{0: [{'a':1,'b':2},{'c':3}]},{1:[]}]) {
  console.log(child)
}

Armfoot
la source
J'ai voté pour. Quelle contorsion, la syntaxe ES6, cependant ... me donne envie de pleurer, et je viens d'un fond C ++ ...
Freddo
1

Vous pouvez vérifier si vous avez tapé forEach correctement, si vous avez tapé foreach comme dans d'autres langages de programmation, cela ne fonctionnera pas.

Abdelsalam Megahed
la source
0

Vous pouvez utiliser à la childNodesplace de children, childNodesest également plus fiable compte tenu des problèmes de compatibilité du navigateur, plus d'informations ici :

parent.childNodes.forEach(function (child) {
    console.log(child)
});

ou en utilisant l'opérateur d'étalement:

[...parent.children].forEach(function (child) {
    console.log(child)
});
Syed
la source