Как преобразовать список узлов DOM в массив в Javascript? - PullRequest
78 голосов
/ 29 апреля 2010

У меня есть функция Javascript, которая принимает список узлов HTML, но она ожидает массив Javascript (на нем запускаются некоторые методы Array), и я хочу передать ей вывод Document.getElementsByTagName, который возвращает список узлов DOM.

Изначально я думал об использовании чего-то простого, например:

Array.prototype.slice.call(list,0)

И это прекрасно работает во всех браузерах, кроме, конечно, Internet Explorer, который возвращает ошибку «Ожидается объект JScript», поскольку очевидно, что список узлов DOM, возвращаемый методами Document.getElement*, не является объектом JScript, достаточным для того, чтобы быть целью вызов функции.

Предостережения: я не против написания специального кода для Internet Explorer, но мне не разрешено использовать какие-либо библиотеки Javascript, такие как JQuery, потому что я пишу виджет для встраивания в сторонний веб-сайт и не могу загрузить внешние библиотеки, которые создадут конфликт для клиентов.

Мое последнее желание - перебрать список узлов DOM и создать сам массив, но есть ли лучший способ сделать это?

Ответы [ 7 ]

108 голосов
/ 18 мая 2016

В es6 вы можете просто использовать следующее:

  • Оператор спреда

     var elements = [... nodelist]
    
  • Использование Array.from

     var elements = Array.from(nodelist)
    

больше ссылок на https://developer.mozilla.org/en-US/docs/Web/API/NodeList

46 голосов
/ 29 апреля 2010

Списки узлов: хост-объекты , использование метода Array.prototype.slice на хост-объектах не гарантирует работу, спецификация ECMAScript сообщает:

Возможность успешного применения функции слайса к хост-объекту зависит от реализации.

Я бы порекомендовал вам сделать простую функцию для итерации по NodeList и добавить каждую существующий элемент в массиве:

function toArray(obj) {
  var array = [];
  // iterate backwards ensuring that length is an UInt32
  for (var i = obj.length >>> 0; i--;) { 
    array[i] = obj[i];
  }
  return array;
}

UPDATE:

Как показывают другие ответы, теперь вы можете использовать в современных средах синтаксис распространения или метод Array.from:

const array = [ ...nodeList ] // or Array.from(nodeList)

Но, подумав об этом, я думаю, что наиболее распространенный вариант использования для преобразования NodeList в массив - это итерация по нему, и теперь у объекта NodeList.prototype есть метод forEach, изначально если вы находитесь в современной среде, вы можете использовать ее напрямую или использовать pollyfill.

16 голосов
/ 13 декабря 2015

Используя распространение (ES2015) , это так же просто, как: [...document.querySelectorAll('p')]

(необязательно: используйте Babel для преобразования вышеуказанного кода ES6 в синтаксис ES5)


Попробуйте в консоли вашего браузера и увидите магию:

for( links of [...document.links] )
  console.log(links);
9 голосов
/ 22 декабря 2015

Используйте этот простой трюк

<Your array> = [].map.call(<Your dom array>, function(el) {
    return el;
})
6 голосов
/ 27 июля 2013

Несмотря на то, что это не совсем правильный шим, так как нет спецификации, требующей работы с элементами DOM, я сделал так, чтобы вы могли использовать slice() следующим образом: https://gist.github.com/brettz9/6093105

ОБНОВЛЕНИЕ : Когда я поднял это с помощью редактора спецификации DOM4 (спрашивая, могут ли они добавить свои собственные ограничения к объектам хоста), чтобы спецификация требовала от разработчиков правильного преобразования этих объектов при использовании с методы массива (помимо спецификации ECMAScript, которая учитывала независимость от реализации), он ответил, что «Хост-объекты более или менее устарели для ES6 / IDL». В http://www.w3.org/TR/WebIDL/#es-array я вижу, что спецификации могут использовать этот IDL для определения «объектов массива платформы», но http://www.w3.org/TR/domcore/, похоже, не использует новый IDL для HTMLCollection (хотя, похоже, что он может сделать это для Element.attributes, хотя в нем только явно указано, что он использует WebIDL для DOMString и DOMTimeStamp). Я вижу, что [ArrayClass] (который наследуется от Array.prototype) используется для NodeListNamedNodeMap теперь устарел в пользу единственного элемента, который все еще будет его использовать, Element.attributes). В любом случае, похоже, это станет стандартом. ES6 Array.from также может быть более удобным для таких преобразований, чем указание Array.prototype.slice и более семантически понятным, чем [].slice() (а более короткая форма, Array.slice() («универсальный массив»)), поскольку Я знаю, не стало стандартным поведением).

3 голосов
/ 02 июля 2018

Сегодня, в 2018 году, мы могли бы использовать ECMAScript 2015 (6-е издание) или ES6, но не все браузеры могут это понять (например, IE не понимает все это). Если вы хотите, вы можете использовать ES6 следующим образом: var array = [... NodeList]; ( в качестве оператора спреда ) или var array = Array.from(NodeList);.

В другом случае (если вы не можете использовать ES6) вы можете использовать кратчайший способ преобразования NodeList в Array:

var array = [].slice.call(NodeList, 0);.

Например:

var nodeList = document.querySelectorAll('input');
//we use "{}.toString.call(Object).slice(8, -1)" to find the class name of object
console.log({}.toString.call(nodeList).slice(8, -1)); //NodeList

var array = [].slice.call(nodeList, 0);
console.log({}.toString.call(array).slice(8, -1)); //Array

var result = array.filter(function(item){return item.value.length > 5});

for(var i in result)
  console.log(result[i].value); //credit, confidence
<input type="text" value="trust"><br><br>
<input type="text" value="credit"><br><br>
<input type="text" value="confidence">

Но если вы хотите просто перебрать список узлов DOM, вам не нужно преобразовывать NodeList в Array. Можно зациклить элементы в NodeList, используя:

var nodeList = document.querySelectorAll('input');
// Calling nodeList.item(i) isn't necessary in JavaScript
for(var i = 0; i < nodeList.length; i++)
    console.log(nodeList[i].value); //trust, credit, confidence
<input type="text" value="trust"><br><br>
<input type="text" value="credit"><br><br>
<input type="text" value="confidence">

Не поддавайтесь соблазну использовать for...in или for each...in для перечисления элементов в списке, поскольку это также перечислит длину и свойства элемента NodeList и вызовет ошибки, если ваш сценарий предполагает, что он имеет дело только с объектными элементами. Кроме того, for..in не гарантируется посещение свойств в любом конкретном порядке. for...of Циклы будут правильно зацикливать объекты NodeList.

См. Также:

3 голосов
/ 29 апреля 2010
var arr = new Array();
var x= ... get your nodes;

for (i=0;i<x.length;i++)
{
  if (x.item(i).nodeType==1)
  {
    arr.push(x.item(i));
  }
}

Это должно сработать, пересечь браузер и получить все узлы-элементы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...