forEach no es un error de función con la matriz de JavaScript

145

Estoy tratando de hacer un bucle simple:

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

Pero me sale el siguiente error:

VM384: 53 Error de tipo no capturado: parent.children.forEach no es una función

Aunque los parent.childrenregistros:

ingrese la descripción de la imagen aquí

¿Cual podría ser el problema?

Nota: Aquí hay un JSFiddle .

alexchenco
fuente
El mismo problema ocurre con element.siblings
Daut
@Daut sí porque element.siblings devuelve una colección HTMLC y las colecciones HTMLC no tienen el método forEach ()
Freddo
1
¡Hola, buscador de Google! si estás leyendo este doble chequeo de que es para cada uno con una E mayúscula en lugar de foreach ...
Robert Sinclair

Respuestas:

127

Primera opción: invocar forEach indirectamente

El parent.childrenes un objeto tipo Array. Use la siguiente solución:

const parent = this.el.parentElement;

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

El tipo parent.childrenes NodeList, que es un objeto tipo Array porque:

  • Contiene la lengthpropiedad, que indica el número de nodos.
  • Cada nodo es un valor de propiedad con nombre numérico, comenzando desde 0: {0: NodeObject, 1: NodeObject, length: 2, ...}

Ver más detalles en este artículo .


Segunda opción: usar el protocolo iterable

parent.childrenes un HTMLCollection: que implementa el protocolo iterable . En un entorno ES2015, puede usarlo HTMLCollectioncon cualquier construcción que acepte iterables.

Usar HTMLCollectioncon el operador de propagación:

const parent = this.el.parentElement;

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

O con el for..ofciclo (que es mi opción preferida):

const parent = this.el.parentElement;

for (const child of parent.children) {
  console.log(child);
}
Dmitri Pavlutin
fuente
Cuando uso su solución no tengo más problemas, pero el código dentro de la función anónima no se ejecuta. .so ..
Jérémy
Qué navegador utiliza para que parent.children le diga que es una lista de nodos. En Firefox, me dice que es una colección HTMLC. Si fuera una lista de nodos, .forEach () funcionaría
Freddo
104

parent.childrenNo es una matriz. Es una colección HTMLC y no tiene forEachmétodo. Puedes convertirlo a la matriz primero. Por ejemplo en ES6:

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

o usando el operador de propagación:

[...parent.children].forEach(function (child) {
    console.log(child)
});
madox2
fuente
9
Prefiero esta solución mucho más que jugar con el prototipo Array.
Daut
Y esta respuesta es (una de) las respuestas correctas a la pregunta de los OP. parent.children es una colección HTMLC que no tiene un método .forEach
Freddo
18

parent.childrendevolverá una lista de lista de nodos , técnicamente una colección html . Esa es una matriz como objeto, pero no una matriz, por lo que no puede llamar directamente a las funciones de matriz sobre ella. En este contexto, puede usar Array.from()para convertir eso en una matriz real,

Array.from(parent.children).forEach(child => {
  console.log(child)
})
Rajaprabhu Aravindasamy
fuente
No, parent.children no devuelve una lista de nodos sino una colección HTML. No es lo mismo. Si fuera una lista de nodos, .forEach funcionaría
Freddo
12

Una versión más ingenua , al menos está seguro de que funcionará en todos los dispositivos, sin conversión y ES6:

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

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

Vaquero
fuente
2
Votaron porque todas estas nuevas funciones de ES6 hacen exactamente lo mismo que estaban disponibles, pero de una manera desordenada, como siempre con JS
Freddo
8

parent.childrenes un HTMLCollectionobjeto tipo matriz. Primero, debe convertirlo en un método real Arraypara usar Array.prototype.

const parent = this.el.parentElement
console.log(parent.children)
[].slice.call(parent.children).forEach(child => {
  console.log(child)
})
Dmitriy
fuente
2
O no lo convierta, pero use use .call () en .forEach ()?
nnnnnn
@nnnnnn Mira mi respuesta a continuación.
Dmitri Pavlutin
Hay muchas maneras de convertir un objeto tipo matriz en una matriz :) Esta es una de ellas
Dmitriy
@DmitriyLoskutov No es necesario convertirlo: JavaScript es un lenguaje de escritura de pato. Solo usa esta función.
Dmitri Pavlutin
5

Esto se debe a que parent.childrenes una NodeList y no es compatible con el .forEachmétodo (ya que NodeList es una estructura similar a una matriz pero no una matriz), por lo tanto, intente llamarla primero convirtiéndola en matriz usando

var children = [].slice.call(parent.children);
children.forEach(yourFunc);
Ammar Hasan
fuente
No, no es una NodeList, es una colección HTML
Freddo
5

No hay necesidad deforEach , puedes iterar usando solo el fromsegundo parámetro de la siguiente manera:

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

Brazo
fuente
La triste noticia es que parent.children no es una lista de nodos ... .from () no funcionará.
Freddo
@Cedric si su objeto no es una NodeList, entonces debe hacer una nueva pregunta específicamente para abordarla. Aquí, el downvoting se usa cuando la respuesta es intrínsecamente incorrecta o dañina y, como puede ver en el fragmento de código, todos los elementos del objeto se repiten e imprimen, que era el objetivo de la pregunta del OP.
Footfoot
Sí, el problema es que la pregunta del OP se relacionó con una colección HTML, no con una lista de nodos ... Entonces, la respuesta fue simplemente no responder la pregunta
Freddo
@Cedric, esta respuesta también iterará sobre una colección HTML, ya que Array.fromconvierte el objeto dado en el primer parámetro en una matriz. El resultado es el mismo que en la respuesta de madox2 sin necesidad de un forEachbucle adicional ( documentos Array.fromMDN ).
Armfoot
4

Si está tratando de recorrer un sitio NodeListcomo este:

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

Recomiendo encarecidamente bucle de esta manera:

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

Personalmente, he intentado varias formas, pero la mayoría de ellas no funcionaron, ya que quería hacer un bucle NodeList, pero esta funciona como un encanto, ¡pruébalo!

No NodeListes una matriz, pero la tratamos como una matriz, por lo Array.tanto, debe saber que no es compatible con navegadores antiguos.

¿Necesita más información sobre NodeList? Por favor lea su documentación en MDN .

Elharony
fuente
1
Esta respuesta obviamente funciona en nodeList. El problema es parent.children devuelve una colección HTML, que no es una lista de nodos ...
Freddo
3

Como está utilizando las funciones de ES6 ( funciones de flecha ), también puede simplemente usar un bucle for como este:

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

Brazo
fuente
Votado Qué contorsión, la sintaxis de ES6, aunque ... Me dan ganas de llorar, y vengo de un fondo C ++ ...
Freddo
1

Puedes verificar si escribiste correctamente para Cada , si escribiste foreach como en otros lenguajes de programación, no funcionará.

Abdelsalam Megahed
fuente
0

Puede usar en childNodeslugar de children, childNodestambién es más confiable teniendo en cuenta los problemas de compatibilidad del navegador, más información aquí :

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

o usando el operador de propagación:

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