En Javascript, ¿cuál es la mejor manera de convertir una NodeList en una matriz?

82

El método DOM document.querySelectorAll()(y algunos otros) devuelve un NodeList.

Para operar en la lista, por ejemplo, usando forEach(), NodeListprimero debe convertirse a un Array.

¿Cuál es la mejor manera de convertir el NodeListen un Array?

cc joven
fuente
1
Creo que el valor de retorno de querySelectorAll () se llama técnicamente NodeList.
jfriend00
from mdm "elementList = document.querySelectorAll (selectores);"
cc young
1
elementList es el nombre de la variable. Esa misma página describe cómo el tipo de valor de retorno es una NodeList.
jfriend00
gracias por la corrección - solucionado en cuestión
cc young

Respuestas:

67

Con ES6 simplemente puede hacer:

const spanList = [...document.querySelectorAll("span")];
Freezystem
fuente
Esto le da a míType 'NodeListOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
de devolución de llamada
Hola @callback, esto parece un error relacionado con TypeScript. Es posible que haya elegido apuntar a la compilación es6 sin agregar "es6" en la matriz lib de su archivo tsconfig. Saludos
Freezystem
1
Hola @Freezystem, ¡tienes razón! Estoy apuntando a es2015. ¡Gracias!
devolución de llamada
@callback ES6 y ES2015 son lo mismo
DarkNeuron
65

Con ES6 puedes usar Array.from(myNodeList). Luego usa tu método de matriz favorito.

var myNodeList = document.querySelectorAll('.my-selector');

// ALT 1
Array.from(myNodeList).forEach(function(el) {
  console.log(el);
});

Use una corrección ES6 para que esto funcione también en navegadores más antiguos.


Si está utilizando un transpilador (por ejemplo, Babel), hay dos alternativas más:

var myNodeList = document.querySelectorAll('.my-selector');

// ALT 2
for (var el of myNodeList) {
  el.classList.add('active'); // or some other action
}

// ALT 3
[...myNodeList].forEach((el) => {
  console.log(el);
});
Sandstrom
fuente
¿No es también cierto en es6 que una lista de nodos proporciona un iterador?
cc young
4
@ccyoung, pero el iterador no funciona en navegadores ES6 no compatibles porque no se puede ajustar el objeto Symbol, por lo que es mejor usarlo Array.from(myNodeList)porque se puede ajustar.
Roc
entonces tengo este problema donde Array.from (el.childNodes) no devuelve el primer nodo como parte de la matriz.
zinoadidi
49

Puede convertirlo en una matriz utilizando el slicemétodo del Arrayprototipo:

var elList = document.querySelectorAll('.viewcount');
elList = Array.prototype.slice.call(elList, 0);

Por otra parte, si todo lo que necesita es forEach, puede invocar que desde el Arrayprototipo, sin coaccionar a una matriz en primer lugar:

var elList = document.querySelectorAll('.viewcount');
Array.prototype.forEach.call(elList, function(el) {
    console.log(el);
});

En ES6, puede usar la nueva Array.fromfunción para convertirlo en una matriz:

Array.from(elList).forEach(function(el) {
    console.log(el);
});

Actualmente, esto solo se encuentra en los navegadores más avanzados, pero si está utilizando un servicio de polyfill , tendrá acceso a esta función en todos los ámbitos.


Si está usando un transpilador ES6 , incluso puede usar un for..ofbucle en su lugar:

for (var element of document.querySelectorAll('.some .elements')) {
  // use element here
}
Joseph Silber
fuente
Gracias. bajo el pensamiento javascript más nuevo / esperando que hubiera una coerción más sucinta.
cc young
6
@cc young: tenga en cuenta que la razón por la que estoy usando en Array.prototype.forEachlugar de [].forEach, es porque este último crea un nuevo objeto Array, que es totalmente innecesario.
Joseph Silber
@JosephSilber ahh, gracias, esa es la nueva matriz que se creó, ¿está vacía []? Mi pensamiento es que se recolectaría basura y el impacto en la memoria es insignificante, ¿alguien puede comentar sobre esto?
Daniel Sokolowski
@Daniel, esto es cierto, pero aún queda el cálculo de crear y destruir la matriz.
Brett
21

¿Por qué convertir? - solo callfunciona con Array directamente en la colección de elementos;)

[].forEach.call( $('a'), function( v, i) {
    // do something
});

asumiendo que $ es su alias para querySelectorAll , por supuesto


editar: ES6 permite una sintaxis aún más corta [...$('a')]( funciona solo en Firefox, a partir de mayo de 2014 )

c69
fuente
Asumiendo que $es querySelectorAll.
c69
3
Su respuesta implica el uso de jQuery. Si ese es el caso, esta tontería es completamente innecesaria, gracias a .each().
Matt Ball
1
lol, ¿por qué? nada te prohíbe crear alias como function $ ( s ) { return document.querySelectorAll(s); }.
c69
5
Si va a utilizar jQuery, entonces la solución más sucinta es:$('a').each(function(i, v) {...});
jfriend00
2
offtopic: EcmaScript 5 es un estándar desde hace un año, todos los navegadores de la generación actual admiten los nuevos métodos de Arrays, y la pregunta era específicamente sobre el uso de esos métodos en NodeList, también conocida como colección de elementos.
c69
9

¿Tiene que ser así forEach? Simplemente podría usar un forbucle para iterar sobre la lista:

for (var i = 0; i < elementList.length; i++) {
    doSomethingWith(elementlist.item(i));
}
nfechner
fuente
1
+1 por optar por la solución simple que no agrega conversiones de matriz innecesarias. Para su información, en lugar de elementList.item(i), podría usar elementList[i].
jfriend00
4
personalmente, encuentro forEach()un mejor estilo de programación y menos detallado - ymmv
cc young
@cc young: En realidad, estoy de acuerdo contigo. Excepto en casos como este, donde necesitaría ejecutar una conversión para poder usar mi patrón favorito. Eso lo hace torpe y se ve así: "Cuando todo lo que tienes es un martillo, todo comienza a verse como un clavo".
nfechner
Y aquí hay una forma más loca :) for (var oElement, i = 0; oElement = aMenuItemsElements; i++ { console.log(oElement); }
Daniel Sokolowski
El problema aquí es que no puede anidar otro for (var i…)bucle porque el bucle for no crea su propio alcance (como lo hace ahora en C / C ++). Y luego ise mezclan.
Jens
9

Actualización 2020: nodeList.forEach () ahora es un estándar oficial y es compatible con todos los navegadores actuales.

Los navegadores más antiguos pueden usar el polyfill a continuación.

Para operar en la lista en javascript, por ejemplo, usando forEach (), la NodeList debe convertirse en una matriz.

Eso no es cierto. .forEach()funciona en los navegadores actuales. Si falta, puede agregar .forEach () de Array a NodeList y funciona bien:

if ( ! NodeList.prototype.forEach ) {
  NodeList.prototype.forEach = Array.prototype.forEach;
}

Ahora puede ejecutar:

myNodeList.forEach(function(node){...})

Para iterar sobre NodeLists como Arrays.

Esto produce un código mucho más corto y limpio que .call () en todas partes.

mikemaccana
fuente
1
Esta respuesta fue exactamente lo que necesitaba y estas 3 líneas me ahorraron mucho tiempo. Todas las otras respuestas habrían requerido cambiar un montón de código y no entiendo por qué.
Scribblemacher
Los votos negativos probablemente se debieron a que los prototipos incorporados de parches de mono se consideran una mala práctica
DuBistKomisch
@DuBistKomisch Este es un polyfill, solo se aplica si el estándar NodeList.foreach () no existe.
mikemaccana
1
ah mi mal, no me di cuenta de que en realidad agregaron forEachespecíficamente, vine aquí buscandofilter
DuBistKomisch
@DuBistKomisch puede usar la misma técnica filter, pero es posible que desee darle un nombre no oficial NodeList.prototype.dbkFiltero similar si le preocupa un estándar futuro que use una implementación diferente.
mikemaccana
2

ES6 permite formas geniales como, var nodeArray = Array.from(nodeList)pero mi favorita es el nuevo operador de propagación.

var nodeArray = Array(...nodeList);
Redu
fuente
¡Una solución perfecta! Esta solución también se aplica a Typecript.
Nirus
Me gustaría agregar que esto solo funciona con TypeScript siempre que no se transpile a ES5 o inferior.
Rudey
2

Eso funcionó conmigo en ES6

supongamos que tienes una lista de nodos como esa

<ul>
  <li data-time="5:17">Flexbox video</li>
  <li data-time="8:22">Flexbox video</li>
  <li data-time="3:24">Redux video</li>
  <li data-time="5:17">Flexbox video</li>
  <li data-time="7:17">Flexbox video</li>
  <li data-time="4:17">Flexbox video</li>
  <li data-time="2:17">Redux video</li>
  <li data-time="7:17">Flexbox video</li>
  <li data-time="9:54">Flexbox video</li>
  <li data-time="5:53">Flexbox video</li>
  <li data-time="7:32">Flexbox video</li>
  <li data-time="2:47">Redux video</li>
  <li data-time="9:17">Flexbox video</li>

</ul>


const items = Array.from(document.querySelectorAll('[data-time]'));

console.log(items);
Amr.Ayoub
fuente
2

Utilizo lo siguiente porque creo que es más fácil de leer:

const elements = document.getElementsByClassName('element');
[...elements].forEach((element) => {
   // code
});

puntilla
fuente
1

Bueno, esto también funciona para mí:

const elements = Object.values( document.querySelector(your selector here) )

Object.values()devoluciones Arrayde valores de un objeto dado. NodeListes objeto, como todo en JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/values

Pero no es compatible con IE, así que supongo que Array.prototype.*array_method*.call(yourNodeList)es la mejor opción. Con esto puede invocar cualquier método de matriz en suNodeList

Rene Stefancik
fuente