Jaki jest najlepszy sposób konwersji NodeList na tablicę w JavaScript?

84

Metoda DOM document.querySelectorAll()(i kilka innych) zwraca plik NodeList.

Aby operować na liście, np. Używając forEach(), NodeListnależy najpierw przekonwertować plik Array.

Jaki jest najlepszy sposób konwertowania NodeListpliku na Array?

cc młody
źródło
1
Myślę, że wartość zwracana przez querySelectorAll () jest technicznie nazywana NodeList.
jfriend00
from mdm "elementList = document.querySelectorAll (selektory);"
cc młody
1
elementList to nazwa zmiennej. Ta sama strona opisuje, w jaki sposób typ zwracanej wartości jest NodeList.
jfriend00
dzięki za korektę - poprawione w pytaniu
cc młody

Odpowiedzi:

70

Dzięki ES6 możesz po prostu:

const spanList = [...document.querySelectorAll("span")];
Freezystem
źródło
1
To daje miType 'NodeListOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
oddzwonienie
Cześć @callback, wygląda to na błąd związany z TypeScript. Być może wybrałeś docelową kompilację es6 bez dodawania "es6" do tablicy lib twojego pliku tsconfig. Pozdrawiam
Freezystem
1
Cześć @Freezystem, masz rację! Celuję w es2015. Dzięki!
oddzwonienie
@callback ES6 i ES2015 to to samo
DarkNeuron
66

Z ES6 możesz korzystać Array.from(myNodeList). Następnie użyj swojej ulubionej metody tablicowej.

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

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

Użyj podkładki ES6, aby to działało również w starszych przeglądarkach.


Jeśli używasz transpilera (na przykład Babel), są jeszcze dwie alternatywy:

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
źródło
nie jest również prawdą w ramach es6, że nodeList dostarcza iterator?
cc młody
4
@ccyoung, ale iterator nie działa w niezgodnych przeglądarkach ES6, ponieważ nie można podkładać obiektu Symbol, więc lepiej jest go używać, Array.from(myNodeList)ponieważ można go podłożyć.
Roc
więc mam ten problem, w którym Array.from (el.childNodes) nie zwraca pierwszego węzła jako część tablicy.
zinoadidi
49

Możesz przekonwertować go na tablicę używając slicemetody z Arrayprototypu:

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

Ponadto, jeśli wszystko, czego potrzebujesz forEach, to możesz wywołać to z Arrayprototypu, bez uprzedniego przekształcania go w tablicę:

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

W ES6 możesz użyć nowej Array.fromfunkcji, aby przekonwertować ją na tablicę:

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

Jest to obecnie dostępne tylko w przeglądarkach krwawiących krawędzi, ale jeśli korzystasz z usługi polyfill , będziesz mieć dostęp do tej funkcji na całej planszy.


Jeśli używasz transpilera ES6 , możesz for..ofzamiast tego użyć pętli:

for (var element of document.querySelectorAll('.some .elements')) {
  // use element here
}
Joseph Silber
źródło
dzięki. pod nowszymi myślami javascript / mając nadzieję, że był bardziej zwięzły przymus.
cc młody
6
@cc young - zwróć uwagę, powodem, dla którego używam Array.prototype.forEachzamiast tego [].forEach, jest to, że ten ostatni tworzy nowy obiekt Array, który jest całkowicie niepotrzebny.
Joseph Silber,
@JosephSilber ahh dziękuję - czy nowa utworzona tablica jest pusta []? Uważam, że zbierze śmieci, a wpływ na pamięć jest znikomy, czy ktoś może to skomentować?
Daniel Sokolowski
@Daniel to prawda, ale nadal istnieje obliczenia tworzenia i niszczenia tablicy.
Brett
21

Dlaczego warto konwertować? - po prostu callfunkcja Array bezpośrednio na kolekcji elementów;)

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

zakładając oczywiście, że $ jest twoim aliasem dla querySelectorAll


edycja: ES6 pozwala na jeszcze krótszą składnię [...$('a')]( działa tylko w Firefoksie, od maja 2014 )

c69
źródło
Zakładając, że $tak querySelectorAll.
c69
3
Twoja odpowiedź sugeruje użycie jQuery. Jeśli tak jest, to dzięki .each().
Matt Ball,
1
lol dlaczego ? nic nie zabrania ci tworzenia aliasów typu function $ ( s ) { return document.querySelectorAll(s); }.
c69
5
Jeśli masz zamiar używać jQuery, to bardziej prostym rozwiązaniem jest:$('a').each(function(i, v) {...});
jfriend00
2
offtopic: EcmaScript 5 jest standardem już od roku, wszystkie przeglądarki obecnej generacji obsługują nowe metody tablic, a pytanie dotyczyło w szczególności używania tych metod w NodeList, czyli zbieraniu elementów.
c69
10

Aktualizacja 2020: nodeList.forEach () jest teraz oficjalnym standardem i jest obsługiwany we wszystkich obecnych przeglądarkach.

Starsze przeglądarki mogą używać poniższego wypełnienia.

Aby operować na liście w javascript, np. Używając forEach (), NodeList musi zostać przekonwertowana na Array.

To nieprawda. .forEach()działa w obecnych przeglądarkach. Jeśli go brakuje, możesz użyć polyfill, aby dodać .forEach () z Array do NodeList i działa dobrze:

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

Możesz teraz biegać:

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

Aby iterować po NodeLists, tak jak Arrays.

Daje to znacznie krótszy i czystszy kod niż .call () wszędzie.

mikemaccana
źródło
1
Ta odpowiedź była dokładnie tym, czego potrzebowałem, a te 3 wiersze zaoszczędziły dużo czasu. Wszystkie inne odpowiedzi wymagałyby zmiany części kodu i nie rozumiem dlaczego.
Scribblemacher
głosy negatywne były prawdopodobnie spowodowane tym, że małpa łatanie wbudowanych prototypów jest uważane za złą praktykę
DuBistKomisch
@DuBistKomisch To jest wypełnienie, stosowane tylko wtedy, gdy nie istnieje standardowa lista NodeList.foreach ().
mikemaccana
2
ach mój zły, nie zdawałem sobie sprawy, że faktycznie dodali forEachkonkretnie, przyjechałem tutaj w poszukiwaniufilter
DuBistKomisch
@DuBistKomisch możesz użyć tej samej techniki filter, ale możesz chcieć nadać mu nieoficjalną nazwę NodeList.prototype.dbkFilterlub podobną, jeśli martwisz się o przyszły standard korzystający z innej implementacji.
mikemaccana
9

Czy to musi być forEach? Możesz po prostu użyć forpętli do iteracji po liście:

for (var i = 0; i < elementList.length; i++) {
    doSomethingWith(elementlist.item(i));
}
nfechner
źródło
1
+1 za przejście z prostym rozwiązaniem, które nie dodaje niepotrzebnych konwersji tablic. FYI, zamiast elementList.item(i), możesz po prostu użyć elementList[i].
jfriend00
4
osobiście znajduję forEach()lepszy styl programowania i mniej rozwlekły
cc young
@cc young: Właściwie zgadzam się z tobą. Z wyjątkiem takich przypadków, w których musiałbym przeprowadzić konwersję, aby móc użyć mojego ulubionego wzorca. To sprawia, że ​​jest niezgrabny i wygląda następująco: „Kiedy masz tylko młotek, wszystko zaczyna wyglądać jak gwóźdź”.
nfechner
A oto bardziej zwariowany sposób :)for (var oElement, i = 0; oElement = aMenuItemsElements; i++ { console.log(oElement); }
Daniel Sokolowski
Problem polega na tym, że nie można zagnieździć kolejnej for (var i…)pętli, ponieważ pętla for nie tworzy własnego zakresu (tak jak teraz robi to w C / C ++). A potem isię pomieszali.
Jens
2

ES6 pozwala na fajne sposoby, takie jak, var nodeArray = Array.from(nodeList)ale moim ulubionym jest nowy operator rozprzestrzeniania.

var nodeArray = Array(...nodeList);
Redu
źródło
Idealne rozwiązanie! To rozwiązanie dotyczy również maszynopisu.
Nirus
Chciałbym dodać, że działa to tylko z TypeScript, o ile nie transpilujesz do ES5 lub niższego.
Rudey
2

To zadziałało ze mną w ES6

załóżmy, że masz taką listę węzłów

<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
źródło
2

Używam następujących, ponieważ wydaje mi się, że najłatwiej jest to przeczytać:

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

ćwiek
źródło
2

Cóż, to działa również dla mnie:

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

Object.values()zwraca Arraywartości danego obiektu. NodeListjest obiektem, tak jak wszystko w JS.

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

Ale nie jest kompatybilny z IE, więc myślę, że Array.prototype.*array_method*.call(yourNodeList)jest to najlepsza opcja. Dzięki temu możesz wywołać dowolną metodę tablicową na swoimNodeList

Rene Stefancik
źródło