DOM-дерево и навигация #
DOM-дерево — иерархическое представление HTML-документа, где каждый тег, текст и комментарий становится узлом (node). JavaScript может перемещаться по связям «родитель — потомок — сосед» и так находить нужные части интерфейса без повторного поиска по всей странице.
Типы узлов #
Основные типы, с которыми работает UI-код:
| Тип | Константа | Пример |
|---|---|---|
| Элемент | Node.ELEMENT_NODE (1) | <section class="widget"> |
| Текст | Node.TEXT_NODE (3) | "3 трека в очереди" |
| Комментарий | Node.COMMENT_NODE (8) | <!-- skeleton --> |
| Документ | Node.DOCUMENT_NODE (9) | #document |
const trackTitle = document.querySelector('.track-title')
trackTitle.nodeType // 1
trackTitle.nodeName // "H3"
trackTitle.textContent // текст внутри, включая вложенные элементы
Родители и предки #
Подняться от элемента к контейнеру:
const removeBtn = document.querySelector('[data-action="remove-track"]')
removeBtn.parentElement // <li class="track-row">
removeBtn.closest('.playlist-panel') // ближайший предок с классом
closest удобен в списках плейлиста: кнопка знает только себя, а обработчик находит строку целиком:
function getTrackRow(button) {
return button.closest('[data-track-id]')
}
parentNode и parentElement почти эквивалентны для элементов; у узла документа родителя нет.
Потомки: childNodes и children #
childNodes — все дочерние узлы, включая текстовые (пробелы между тегами тоже узлы):
const cartList = document.querySelector('.cart-items')
cartList.childNodes.length // может быть больше числа <li>
children — только дочерние элементы (HTMLCollection):
const widgets = document.querySelector('.dashboard-grid').children
widgets[0] // первый виджет-колонка
Первый и последний элемент-потомок:
const grid = document.querySelector('.dashboard-grid')
grid.firstElementChild
grid.lastElementChild
Соседние узлы #
Горизонтальная навигация по разметке:
const activeWidget = document.querySelector('.widget.is-active')
activeWidget.nextElementSibling // следующий виджет в сетке
activeWidget.previousElementSibling
nextSibling / previousSibling учитывают текстовые узлы; для вёрстки надёжнее варианты с Element.
Обход дерева на практике #
Разметка виджета KPI в JSFlow:
<section class="widget" data-widget="kpi">
<header class="widget__head">
<h2>Конверсия</h2>
<span class="widget__badge">live</span>
</header>
<p class="widget__value">4.2%</p>
</section>
Навигация без повторного querySelector:
const widget = document.querySelector('[data-widget="kpi"]')
const badge = widget.querySelector('.widget__badge')
const valueEl = widget.lastElementChild
badge.textContent // "live"
valueEl.textContent // "4.2%"
Если структура стабильна, querySelector внутри контейнера читается проще, чем цепочка children[0].children[1].
NodeList и HTMLCollection #
Результаты обхода часто «живые»: при изменении DOM коллекция обновляется автоматически.
const rows = document.querySelector('.checkout-summary').children
console.log(rows.length)
// добавили строку — length изменится без повторного запроса
querySelectorAll возвращает статический NodeList — снимок на момент вызова. При динамическом списке товаров перезапрашивайте или используйте делегирование событий.
Итог #
- DOM — дерево узлов: элементы, текст, комментарии.
- Вверх:
parentElement,closest; вниз:children,firstElementChild. - Вбок:
nextElementSibling,previousElementSibling. children— только элементы;childNodes— все узлы, включая пробелы.- Для списков и форм комбинируйте локальный
querySelectorвнутри найденного контейнера.