Как лучше всего преобразовать NodeList в массив в JavaScript?

84

Метод DOM document.querySelectorAll()(и некоторые другие) возвращаютNodeList .

Чтобы работать со списком, например, используя forEach(), NodeListнеобходимо сначала преобразовать вArray .

Какой самый лучший способ преобразовать NodeListАня Array?

cc молодой
источник
1
Я думаю, что возвращаемое значение querySelectorAll () технически называется NodeList.
jfriend00
из mdm "elementList = document.querySelectorAll (selectors);"
cc young
1
elementList - это имя переменной. На этой же странице описывается, как тип возвращаемого значения - это NodeList.
jfriend00
спасибо за исправление - исправлено в вопросе
cc young

Ответы:

70

С ES6 вы можете просто:

const spanList = [...document.querySelectorAll("span")];
Freezystem
источник
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);
});
Sandstrom
источник
также неверно в es6, что nodeList предоставляет итератор?
cc young
4
@ccyoung, но итератор не работает в несовместимых браузерах ES6, потому что вы не Array.from(myNodeList)можете использовать оболочку Symbol для объекта, поэтому его лучше использовать, потому что его можно использовать.
Roc
поэтому у меня проблема, когда 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);
});

В настоящее время это есть только в современных браузерах, но если вы используете службу полифиллов, у вас будет доступ к этой функции во всех направлениях.


Если вы используете транспилятор 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 г. )

c69
источник
Предполагая , что $есть querySelectorAll.
c69
3
Ваш ответ подразумевает использование jQuery. Если это так, то в этом дурачестве нет никакой необходимости, спасибо .each().
Мэтт Болл,
1
Смешно. Почему ? ничто не запрещает делать псевдонимы вроде function $ ( s ) { return document.querySelectorAll(s); }.
c69
5
Если вы собираетесь использовать jQuery, то более лаконичное решение:$('a').each(function(i, v) {...});
jfriend00
2
offtopic
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 так же, как Arrays.

Это дает намного более короткий и чистый код, чем везде .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 за простое решение, которое не добавляет ненужных преобразований массивов. К вашему сведению, вместо этого 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перепутались.
Jens
2

ES6 позволяет использовать такие классные способы, как, var nodeArray = Array.from(nodeList)но мой любимый - новый оператор распространения.

var nodeArray = Array(...nodeList);
Уменьшить
источник
Прекрасное решение! Это решение применимо и к Typescript.
Нирус
Я хотел бы добавить, что это работает только с 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

Пер Кестед Аронссон
источник