Перейти к содержимому

DOM: traversal, create, manipulate

DOM API — низкоуровневый интерфейс браузера к живому дереву документа; знание разницы между innerHTML, textContent и insertAdjacentHTML разделяет джунов от сеньоров и постоянно всплывает в вопросах о XSS и производительности рендеринга.

// Traversal: современный vs legacy API
const nav = document.querySelector('nav.main'); // CSS-селектор
const items = nav.querySelectorAll(':scope > li'); // :scope — только прямые дети
const first = nav.firstElementChild; // пропускает TextNode
const par = first.closest('.layout'); // поднимаемся вверх до совпадения
// Итерация без Array.from (NodeList iterable в ES2015+)
for (const li of items) li.classList.toggle('active');
// parentNode vs parentElement:
// у document.documentElement parentNode = document, parentElement = null
// children vs childNodes: children — только Element, childNodes — все узлы (Text, Comment)
// Создание и вставка узлов с минимальным числом reflow
const frag = document.createDocumentFragment(); // один reflow вместо N
const data = ['Alice', 'Bob', 'Carol'];
data.forEach(name => {
const li = document.createElement('li');
li.textContent = name; // безопасно, не парсит HTML
frag.appendChild(li);
});
document.getElementById('list').appendChild(frag);
// insertAdjacentHTML: 4 позиции — не заменяет существующий контент
el.insertAdjacentHTML('beforeend', '<span class="badge">new</span>');
el.insertAdjacentHTML('afterbegin', '<span class="icon">★</span>');
// 'beforebegin' | 'afterbegin' | 'beforeend' | 'afterend'
// cloneNode(true) — глубокая копия со всеми потомками и атрибутами
const clone = document.querySelector('.card').cloneNode(true);
clone.removeAttribute('id'); // id должен быть уникальным
document.querySelector('.grid').appendChild(clone);
// dataset, classList, атрибуты: правильные API
const btn = document.querySelector('[data-id]');
console.log(btn.dataset.id); // 'data-id' → dataset.id (camelCase)
btn.dataset.loading = 'true'; // устанавливает data-loading
btn.dataset.userId = '42'; // → data-user-id="42"
btn.classList.add('active', 'visible'); // variadic
btn.classList.replace('visible', 'hidden');
btn.classList.toggle('open', isOpen); // force-флаг: true=add, false=remove
// setAttribute vs DOM-свойство
input.setAttribute('disabled', ''); // атрибут (строка)
input.disabled = true; // свойство DOM (boolean)
// getAttribute('disabled') → '' даже если disabled установлен через свойство
// innerHTML vs outerHTML vs textContent
el.textContent = rawStr; // автоэкранирует HTML — безопасно
el.innerHTML = trustedHTML; // парсит — XSS если userInput!
console.log(el.outerHTML); // сериализует сам элемент + содержимое

Итог: DOM API предоставляет полный набор методов для обхода, создания и мутации дерева; правильный выбор метода вставки критичен для безопасности (XSS) и производительности (reflow).