В чем разница между document.getElementsByClassName и querySelectorAll в JavaScript
Что проверяют
Знаете ли вы разницу между живой и статической коллекцией и чем она оборачивается в реальном коде. Следом обычно просят показать цикл, который ломается на живой коллекции, или спрашивают, какой метод быстрее и важно ли это вообще.
Как отвечать
Сравнивайте по четырём пунктам:
- что принимают:
getElementsByClassNameполучает имя класса или несколько имён через пробел ('card active'найдёт элементы с обоими классами).querySelectorAllпринимает любой CSS-селектор с комбинаторами, атрибутами и псевдоклассами, а на невалидном селекторе бросаетSyntaxError; - что возвращают: первый отдаёт
HTMLCollection, живую коллекцию, которая сама отражает изменения DOM. Второй отдаётNodeList, снимок на момент вызова; - как перебирать: у
NodeListестьforEach, уHTMLCollectionего нет. Зато обе коллекции итерируемы, поэтому работаютfor...of, спред иArray.from; - скорость:
getElementsByClassNameвозвращается мгновенно, потому что коллекция ленивая и браузер её кеширует, а поиск происходит при обращении к элементам. На обычной странице разницы не видно, выбирать стоит по удобству.
Оба метода есть не только у document, но и у любого элемента, тогда поиск идёт внутри его поддерева. В современном коде чаще берут querySelectorAll: один синтаксис с CSS и предсказуемый снимок. getElementsByClassName оправдан, когда нужна именно коллекция, которая следит за DOM без повторных запросов.
Где спотыкаются
Снимают класс в цикле по живой коллекции: for (let i = 0; i < list.length; i++) list[i].classList.remove('active'). Элемент выпадает из коллекции, индексы сдвигаются, и обрабатывается только каждый второй. Лечится копией через Array.from или обходом с конца.
Говорят, что NodeList всегда статичен. Это верно только для querySelectorAll: childNodes тоже возвращает NodeList, но живой.
Не знают, что el.querySelectorAll('div p') сопоставляет селектор со всем документом и лишь потом оставляет потомков el. В результат попадут абзацы внутри el, чей предок div находится снаружи. Чтобы ограничить поиск, пишут el.querySelectorAll(':scope div p').