JavaScript: HTMLCollection, NodeList и массив объектов


Перевод “HTMLCollection, NodeList and array of objects”


HTMLCollection, NodeList и массив объектов в JavaScript

Предположим, что имеем код DOM указанный ниже. Необходимо получить массив всех дочерних узлов div.container с помощью JavaScript.

<div id=”container”>
<div class="divy">...</div>
<div class="divy">...</div>
<div class="divy">...</div>
<div class="divy">...</div>
</div>

Если вы любите jQuery, то вы могли бы предложить такой код:

$('.divy')
// или
$('#container').children()

Я пытался сделать это используя нативный селектор DOM и вот, что получилось:

const childDivs = document.querySelectorAll('.divy')
Array.isArray(childDivs) //=> false
childDivs.constructor.name //=> NodeList

Ну ладно. Давайте попробуем один из методов getElementBy% и посмотрим, что он возвращает:

const childDivsAgain = document.getElementsByClassName('divy')
Array.isArray(childDivs) //=> false
childDivs.constructor.name //=> HTMLCollection

Что это за объектыNodeList и HTMLCollection и почему мы не получаем “ванильный” массив JavaScript с помощью этих методов?

Попробуем понять разницу между NodeList и HTMLCollection:

HTMLCollection - это список узлов. Отдельный узел может быть доступен по порядковому номеру или имени узла и атрибута.
Коллекции в HTML DOM являются живыми. Коллекции обновляются при изменении документа.

Коллекция HTML всегда находится в DOM, в то время как NodeList является более универсальной конструкцией, которая может или не может быть в DOM.

Объект NodeList является коллекцией узлов. NodeList обеспечивает уровень абстракции над коллекцией узлов, не определяя и не ограничивая как это коллекция используется. Объекты NodeList являются “живыми” или статическим, в зависимости от способа используемого для их получения.

Рассмотрим код ниже:

let parentDiv = document.getElementById('container')
let nodeListDivs = document.querySelectorAll('.divy')
let htmlCollectionDivs = document.getElementsByClassName('divy')
nodeListDivs.length //=> 4
htmlCollectionDivs.length //=> 4
//добавим новый дочерний контейнер
var newDiv = document.createElement('div');
newDiv.className = 'divy'
parentDiv.appendChild(newDiv)
nodeListDivs.length //=> 4
htmlCollectionDivs.length //=> 5

Вы можете видеть, что HTMLCollection учел появление нового узла.Так изменение в DOM автоматически доступны в коллекции.

Но не все объекты NodeList являются статическими. Например, document.getElementByName вернет нам “живой” NodeList.

Но помните, что ни HTMLCollection, ни NodeList не поддерживают методы прототипа массива как push, pop илиsplice. Перебирающий методы, как forEach , были добавлены недавно и уже поддерживаются в новейшими браузерами.

Эти знания мне понадобились, когда я пробовал использовать библиотеку dragula drag-n-drop. Там мне необходимо было добавить выбранные компоненты в библиотеку.

let draggableLists = document.querySelectorAll('div.draggable')
dragula(draggableLists); // твоюж! не работает!

Как и ожидалось, этот код не будет работать. Так как конструктор dragula() ждет на вход массив, а получает NodeList.

Чтобы преобразовать NodeList или HTMLCollection в массив, необходимо сделать одно из следующего:

Использовать метод Array.from

const nodelist = document.querySelectorAll(‘.divy’)
const divyArray = Array.from(nodelist)

Использовать Array.prototype.slice

const nodelist = document.querySelectorAll(‘.divy’)
const divyArray = Array.prototype.slice.call(nodelist)

Использовать ES6

Мне нравится данный способ. Если вы используете ES6, то поможет spread оператор:

const divyArray = […document.querySelectorAll(‘.divy’)]

Добавим этот код в dragula и все заработает:

dragula([...document.querySelectorAll('div.draggable')])

Так когда необходимо преобразовывать NodeList в массив? Это зависит от вашего конкретного случая. Если вы хотите использовать актуальную информацию об узлах в DOM все время, то вы должны использовать NodeList или HTMLCollection как есть, без преобразования в массив.

02.02.2017 Эту страницу просмотрели за все время 10306 раз(а)


Twitter


Облако тегов