Який в JavaScript найкращий спосіб перетворити NodeList на масив?


84

Метод DOM document.querySelectorAll()(і кілька інших) повертає a NodeList.

Щоб оперувати списком, наприклад, використовуючи forEach(), NodeListспочатку потрібно перетворити на Array.

Який найкращий спосіб перетворити NodeListАня Array?


1
Я думаю, що повернене значення querySelectorAll () технічно називається NodeList.
jfriend00

з mdm "elementList = document.querySelectorAll (селектори);"
cc young

1
elementList - ім'я змінної. Ця сама сторінка описує, як тип поверненого значення є NodeList.
jfriend00

дякую за виправлення - виправлено питання
cc young

Відповіді:


70

За допомогою ES6 ви можете просто зробити:

const spanList = [...document.querySelectorAll("span")];

1
Це дає меніType 'NodeListOf<Element>' must have a '[Symbol.iterator]()' method that returns an iterator.ts(2488)
зворотний виклик

Привіт @callback, це здається помилкою, пов’язаною з TypeScript. Можливо, ви вибрали націлювання на компіляцію es6, не додаючи "es6" у масив lib вашого файлу tsconfig. З повагою
Freezystem

1
Привіт @Freezystem, ти маєш рацію! Я націлений на es2015. Дякую!
зворотний дзвінок

@callback ES6 та ES2015 - це одне і те ж
DarkNeuron

66

З ES6 ви можете використовувати Array.from(myNodeList). Потім використовуйте улюблений метод масиву.

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

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

Використовуйте підкладку ES6, щоб це працювало і в старих браузерах.


Якщо ви використовуєте компілятор (наприклад, Babel), є ще дві альтернативи:

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);
});

також не відповідає дійсності під es6, що nodeList постачає ітератор?
cc young

4
@ccyoung, але ітератор не працює в несумісних браузерах ES6, оскільки ви не можете обробляти об'єкт Symbol, тому його краще використовувати, Array.from(myNodeList)оскільки його можна зашитувати.
Рок,

отже, у мене ця проблема, коли Array.from (el.childNodes) не повертає перший вузол як частину масиву.
zinoadidi

49

Ви можете перетворити його в масив, використовуючи sliceметод з Arrayпрототипу:

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

Крім того, якщо все, що вам потрібно forEach, ви можете викликати це з Arrayпрототипу, не примушуючи його спочатку до масиву:

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

У ES6 ви можете використовувати нову Array.fromфункцію для перетворення її в масив:

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

Наразі це лише у браузерах, що перекриваються краями, але якщо ви користуєтеся послугою polyfill, ви матимете доступ до цієї функції по всьому форуму.


Якщо ви використовуєте транспілер ES6 , ви можете навіть використовувати for..ofцикл:

for (var element of document.querySelectorAll('.some .elements')) {
  // use element here
}

Дякую. під новішими думками javascript / сподіваючись, що був більш лаконічний примус.
cc young

6
@cc young - Зверніть увагу, причиною, яку я використовую Array.prototype.forEachзамість цього [].forEach, є те, що останній створює новий об’єкт Array, що абсолютно непотрібно.
Джозеф Сільбер

@JosephSilber ах, дякую - тобто створений новий масив є порожнім []? Я думаю, що це призведе до збирання сміття, а вплив на пам’ять незначний, хтось може це прокоментувати?
Даніель Соколовський

@Daniel це правда, але все ще є обчислення створення та знищення масиву.
Бретт

21

Навіщо конвертувати? - просто callфункція Array безпосередньо на колекції елементів;)

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

припускаючи, що $ - це ваш псевдонім для querySelectorAll , звичайно


редагувати: ES6 дозволяє ще коротший синтаксис [...$('a')]( працює лише у Firefox, станом на травень 2014 року )


Припускаючи, що $є querySelectorAll.
c69

3
Ваша відповідь передбачає використання jQuery. Якщо це так, завдяки цій розладці зовсім непотрібно .each().
Matt Ball

1
ха-ха, чому? ніщо не забороняє вам робити подібні псевдоніми function $ ( s ) { return document.querySelectorAll(s); }.
c69

5
Якщо ви збираєтеся використовувати jQuery, то більш сукцинтне рішення:$('a').each(function(i, v) {...});
jfriend00

2
offtopic: EcmaScript 5 - це стандарт вже рік, усі браузери поточного покоління підтримують нові методи масивів, і питання саме стосувалося використання цих методів у колекції NodeList, яка називається Element.
c69

10

Оновлення 2020: nodeList.forEach () тепер є офіційним стандартом і підтримується у всіх поточних браузерах.

Старіші браузери можуть використовувати поліфіл нижче.

Для роботи зі списком у javascript, наприклад, за допомогою forEach (), NodeList потрібно перетворити на масив.

Це не правда. .forEach()працює в поточних браузерах. Якщо він відсутній, ви можете використовувати поліфіл, щоб додати .forEach () з масиву до NodeList, і він чудово працює:

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

Тепер ви можете запустити:

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

Перебирати NodeLists так само, як і масиви.

Це створює набагато коротший і чистіший код, ніж .call () скрізь.


1
Ця відповідь була саме тим, що мені потрібно, і ці 3 рядки заощадили багато часу. Для всіх інших відповідей потрібно було б змінити купу коду, і я не розумію, чому.
Scribblemacher

проти, можливо, тому, що виправлення мавп вбудованих прототипів вважається поганою практикою
DuBistKomisch

@DuBistKomisch Це полізаповнення, застосовується лише в тому випадку, якщо не існує стандартного NodeList.foreach ().
mikemaccana

2
ах, погано, не зрозумів, що насправді додали forEachспеціально, я прийшов сюди шукатиfilter
DuBistKomisch

@DuBistKomisch ви можете використовувати ту саму техніку filter, але, можливо, ви захочете назвати її неофіційною назвою NodeList.prototype.dbkFilterабо подібною, якщо вас турбує майбутній стандарт із іншою реалізацією.
mikemaccana

9

Це має бути forEach? Ви можете просто використати forцикл для перегляду списку:

for (var i = 0; i < elementList.length; i++) {
    doSomethingWith(elementlist.item(i));
}

1
+1 за використання простого рішення, яке не додає зайвих перетворень масивів. FYI, замість того elementList.item(i), щоб ви могли просто використовувати elementList[i].
jfriend00

4
особисто я вважаю forEach()кращий стиль програмування і менш багатослівний - ymmv
cc young

@cc young: Насправді, я з вами згоден. За винятком таких випадків, коли мені потрібно було б виконати перетворення, щоб я міг використовувати свій улюблений шаблон. Це робить його незграбним і виглядає так: "Коли все, що у вас є - це молоток, все починає виглядати як цвях".
nfechner

А ось і поживніший спосіб :) for (var oElement, i = 0; oElement = aMenuItemsElements; i++ { console.log(oElement); }
Даніель Соколовський

Проблема в тому, що ви не можете вкласти ще один for (var i…)цикл, оскільки цикл for не створює власної області дії (як це робиться зараз на C / C ++). А потім iзмішуються.
Йенс,

2

ES6 дозволяє класні способи, як, var nodeArray = Array.from(nodeList)але мій улюблений - це новий оператор розповсюдження.

var nodeArray = Array(...nodeList);

Ідеальне рішення! Це рішення стосується і Typescript.
Nirus

Я хотів би додати, що це працює лише з TypeScript, якщо ви не перекладаєте на ES5 або нижче.
Руді

2

Це працювало зі мною в ES6

припустимо, у вас є такий номер вузлів

<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);

2

Я використовую наступне, оскільки вважаю, що це найпростіше прочитати:

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


2

Ну, це працює і для мене:

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

Object.values()повертає Arrayзначення даного об'єкта.NodeListє об'єктом, як і все в JS.

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

Але він не сумісний з IE, тому, мабуть, Array.prototype.*array_method*.call(yourNodeList)це найкращий варіант. За допомогою цього ви можете викликати будь-який метод масиву на вашомуNodeList


-1

Якщо припустити, що elems є nodeList:

var elems = document.querySelectorAll('select option:checked');

тоді його можна перетворити в масив наступним чином:

var values = [].map.call(elems, function(obj) {
  return obj.value;
});

Посилання: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Example:_using_map_generically_querySelectorAll

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.