Навигация по DOM
Навигация по DOM — перемещение между узлами DOM-дерева: к родителям, детям и соседним элементам, используя встроенные свойства узлов.
Зачем нужно
Иногда нужно обработать не конкретный элемент, а его соседей, родителя или дочерние элементы. Навигационные свойства позволяют обходить дерево без повторного поиска.
Где используется
- Обход DOM в плагинах и компонентах
- Делегирование событий (поиск родительского контейнера)
- Построение кастомных виджетов (аккордеоны, табы)
- Динамическая вставка элементов рядом с существующими
Предпосылки
Навигация по всем узлам (включая текстовые)
const parent = document.querySelector('.container');
// Дочерние узлы
parent.childNodes; // NodeList всех дочерних узлов (текст, комментарии, элементы)
parent.firstChild; // первый дочерний узел
parent.lastChild; // последний дочерний узел
parent.hasChildNodes; // есть ли дочерние узлы (boolean)
// Родительский узел
parent.parentNode; // родительский узел
// Соседние узлы
parent.nextSibling; // следующий узел-сосед
parent.previousSibling; // предыдущий узел-сосед
// Пример: текстовые узлы между элементами
// <div> <span>A</span> <span>B</span> </div>
const div = document.querySelector('div');
console.log(div.childNodes.length); // 5 (текст, span, текст, span, текст)
console.log(div.firstChild.nodeType); // 3 (Text — пробел/перенос)
Навигация только по элементам (без текстовых узлов)
const parent = document.querySelector('.container');
// Дочерние элементы
parent.children; // HTMLCollection только элементов
parent.firstElementChild; // первый дочерний элемент
parent.lastElementChild; // последний дочерний элемент
parent.childElementCount; // количество дочерних элементов
// Родительский элемент
parent.parentElement; // родительский элемент
// Соседние элементы
parent.nextElementSibling; // следующий элемент-сосед
parent.previousElementSibling; // предыдущий элемент-сосед
// Пример: только элементы
// <div> <span>A</span> <span>B</span> </div>
const div = document.querySelector('div');
console.log(div.children.length); // 2 (только span)
console.log(div.firstElementChild.tagName); // "SPAN"
Сравнение: все узлы vs только элементы
| Все узлы | Только элементы |
|---|---|
parentNode |
parentElement |
childNodes |
children |
firstChild |
firstElementChild |
lastChild |
lastElementChild |
previousSibling |
previousElementSibling |
nextSibling |
nextElementSibling |
// Разница на практике
// <ul>
// <li>Первый</li>
// <li>Второй</li>
// </ul>
const ul = document.querySelector('ul');
// childNodes включает текстовые узлы (переносы строк)
console.log(ul.childNodes.length); // 5 (текст, li, текст, li, текст)
// children — только элементы
console.log(ul.children.length); // 2 (li, li)
// Рекомендация: в 99% случаев используйте Element-версии
Обход дерева
// Обход всех дочерних элементов
function walkChildren(element, callback) {
for (const child of element.children) {
callback(child);
}
}
// Рекурсивный обход всего поддерева
function walkTree(element, callback) {
callback(element);
for (const child of element.children) {
walkTree(child, callback);
}
}
walkTree(document.body, (el) => {
console.log(el.tagName, el.className);
});
// TreeWalker — встроенный API для обхода
const walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT, // только элементы
{
acceptNode: (node) =>
node.classList.contains('highlight')
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_SKIP
}
);
let node;
while (node = walker.nextNode) {
console.log(node.textContent);
}
Навигация по таблицам
// У таблиц есть специальные свойства навигации
const table = document.querySelector('table');
table.rows; // HTMLCollection всех <tr>
table.caption; // элемент <caption>
table.tHead; // элемент <thead>
table.tFoot; // элемент <tfoot>
table.tBodies; // коллекция <tbody>
// Строка
const row = table.rows[0];
row.cells; // HTMLCollection ячеек строки
row.rowIndex; // индекс строки в таблице
row.sectionRowIndex; // индекс внутри секции (thead/tbody/tfoot)
// Ячейка
const cell = row.cells[0];
cell.cellIndex; // индекс ячейки в строке
Навигация по формам
// Формы
document.forms; // все формы
document.forms[0]; // первая форма
document.forms.myForm; // форма с name="myForm"
const form = document.forms[0];
form.elements; // все элементы формы
form.elements.email; // элемент с name="email"
form.elements['user-name']; // элемент с name="user-name"
// Обратная ссылка
const input = form.elements.email;
input.form; // ссылка на родительскую <form>
Частые ошибки
1. Путаница между childNodes и children
const list = document.querySelector('ul');
// childNodes включает текстовые узлы!
list.childNodes[0].tagName; // undefined (это Text-узел)
// children — только Element-узлы
list.children[0].tagName; // "LI"
2. parentNode vs parentElement
// Для обычных элементов разницы нет
const div = document.querySelector('div');
div.parentNode === div.parentElement; // true
// Разница на уровне document
document.documentElement.parentNode; // #document
document.documentElement.parentElement; // null (document — не Element)
3. siblings могут быть null
const first = document.querySelector('li:first-child');
console.log(first.previousElementSibling); // null — нет предыдущего
const last = document.querySelector('li:last-child');
console.log(last.nextElementSibling); // null — нет следующего
// Всегда проверяйте на null
if (first.nextElementSibling) {
first.nextElementSibling.classList.add('highlight');
}
Практика
- Обойди все
<li>элементы списка черезchildrenи выведи текст - Для каждого элемента выведи его родителя и соседей
- Реализуй рекурсивный обход DOM-дерева с подсчётом глубины
- Используй навигацию по таблице для раскрашивания чётных строк