Техники манипуляции DOM в JavaScript
Изучите техники манипуляции DOM: создание, вставку, замену и удаление элементов, а также оптимизацию с помощью пакетных операций, фрагментов и клонирования узлов.
DOM (Document Object Model) манипуляция — важнейший аспект веб-разработки, позволяющий создавать динамичные и интерактивные страницы путём изменения структуры, содержимого и стилей документа средствами JavaScript. В этом руководстве рассматриваются основные техники создания, вставки, замены и удаления элементов, а также передовые практики и методы оптимизации производительности — пакетные обновления, фрагменты документа и клонирование узлов, — которые обеспечивают быструю работу с изменениями.
Если вы ещё не выбрали элемент, начните с выбора DOM-элементов. Чтобы понять дерево, которое вы изменяете, ознакомьтесь с разделом понимание DOM-узлов.
Основные техники манипуляции
Любое изменение DOM относится к одной из четырёх категорий: создание узлов, их вставка, замена или удаление.
Создание элементов
Используйте document.createElement() для создания элемента в памяти, затем задайте его содержимое и атрибуты перед вставкой на страницу. Созданный узел не отображается до тех пор, пока не будет добавлен в документ.
const card = document.createElement('div');
card.className = 'card';
card.textContent = 'Hello, DOM!';
card.setAttribute('data-role', 'greeting');
console.log(card.outerHTML);
// <div class="card" data-role="greeting">Hello, DOM!</div>При вставке обычного текста предпочитайте textContent вместо innerHTML — это быстрее и исключает риск безопасности, связанный с внедрением неэкранированного HTML.
Вставка элементов
После создания элемента прикрепите его с помощью одного из следующих методов:
parent.append(node)— добавляет узел в конец дочерних элементов (также принимает строки).parent.prepend(node)— добавляет его в начало дочерних элементов.target.before(node)/target.after(node)— вставляет соседний узел до или послеtarget.parent.appendChild(node)— классический API; добавляет один узел в конец и возвращает его.parent.insertBefore(node, reference)— вставляетnodeперед дочерним элементомreference.
const list = document.createElement('ul');
const first = document.createElement('li');
first.textContent = 'First';
list.append(first);
const second = document.createElement('li');
second.textContent = 'Second';
list.append(second);
// Put a new item before the first one.
const top = document.createElement('li');
top.textContent = 'Top';
first.before(top);
console.log(list.children.length); // 3
console.log(list.firstElementChild.textContent); // TopДля вставки HTML-строки в точную позицию без повторного разбора всего родителя используйте insertAdjacentHTML():
const wrapper = document.createElement('div');
const box = document.createElement('div');
box.textContent = 'middle';
wrapper.append(box); // box must be in the tree to gain siblings
box.insertAdjacentHTML('beforebegin', '<span>before</span>');
box.insertAdjacentHTML('afterend', '<span>after</span>');
console.log(box.previousElementSibling.textContent); // before
console.log(box.nextElementSibling.textContent); // afterЗамена и удаление элементов
oldNode.replaceWith(newNode)— заменяетoldNodeнаnewNodeна том же месте.element.remove()— отсоединяет элемент от DOM.parent.replaceChild(newNode, oldNode)иparent.removeChild(child)— устаревшие аналоги.
const parent = document.createElement('div');
const a = document.createElement('p');
a.textContent = 'old';
parent.append(a);
const b = document.createElement('p');
b.textContent = 'new';
a.replaceWith(b);
console.log(parent.innerHTML); // <p>new</p>
b.remove();
console.log(parent.innerHTML); // (empty)Современные методы (append, prepend, before, after, replaceWith, remove) читаются проще, чем устаревшие appendChild/insertBefore/replaceChild/removeChild, и принимают одновременно несколько узлов и строк. Отдавайте предпочтение им; прибегайте к устаревшему API только при необходимости поддержки очень старых браузеров.
Передовые практики манипуляции DOM
Минимизируйте прямое обращение к DOM
Обращение к DOM может быть медленным, поскольку оно может заставить браузер пересчитать макет и перерисовать элементы. Чтобы минимизировать прямое обращение к DOM:
- Объединяйте обновления DOM вместо выполнения множества мелких изменений.
- Используйте переменные для хранения ссылок на часто используемые элементы.
Оптимизируйте обработку событий
Прикрепляйте обработчики событий эффективно:
- Используйте делегирование событий, чтобы сократить количество слушателей событий.
- Избегайте прикрепления слишком большого числа слушателей событий непосредственно к элементам.
Удаляйте неиспользуемые элементы
Удаляйте элементы, которые больше не нужны, чтобы освободить память и улучшить производительность:
- Используйте методы
removeChildилиremoveдля удаления элементов из DOM.
Минимизация перекомпоновок и перерисовок
Что такое перекомпоновки и перерисовки?
- Перекомпоновки происходят, когда изменяется макет части страницы, заставляя браузер пересчитывать позиции и размеры элементов.
- Перерисовки случаются, когда меняется визуальный вид элементов без изменения макета (например, изменение цвета).
Техники минимизации перекомпоновок и перерисовок
Пакетные изменения
Объединяйте несколько изменений, чтобы избежать повторяющихся перекомпоновок и перерисовок:
<!DOCTYPE html>
<html>
<head>
<title>Batching Changes</title>
</head>
<body>
<div id="content">Original Content</div>
<button id="update">Update Content</button>
<script>
document.getElementById('update').addEventListener('click', () => {
const content = document.getElementById('content');
content.style.display = 'none'; // Hide element to batch changes
content.innerHTML = 'Updated Content';
content.style.display = 'block'; // Show element after updates
});
</script>
</body>
</html>В этом примере показано, как объединять изменения DOM-элемента для минимизации перекомпоновок и перерисовок. Скрывая элемент перед внесением нескольких изменений и показывая его после, вы можете избежать промежуточных перекомпоновок и перерисовок.
Используйте CSS-классы для изменений
Применяйте изменения CSS с помощью классов вместо прямого управления стилями:
<!DOCTYPE html>
<html>
<head>
<title>Use CSS Classes</title>
<style>
.hidden { display: none; }
.highlight { color: red; font-weight: bold; }
</style>
</head>
<body>
<div id="content">Hello World</div>
<button id="toggle">Toggle Highlight</button>
<script>
document.getElementById('toggle').addEventListener('click', () => {
const content = document.getElementById('content');
content.classList.toggle('highlight');
});
</script>
</body>
</html>Этот пример демонстрирует, как использовать CSS-классы для одновременного применения нескольких изменений стилей, что эффективнее, чем изменение отдельных свойств стиля. Переключение класса, изменяющего несколько стилей, помогает уменьшить количество перекомпоновок и перерисовок.
Использование фрагментов документа для повышения производительности
Что такое фрагмент документа?
Фрагмент документа (Document Fragment) — это лёгкий контейнер для хранения группы узлов. Он не является частью основного DOM-дерева, что означает, что изменения в нём не вызывают перекомпоновок и перерисовок.
При выполнении нескольких DOM-манипуляций используйте DocumentFragment для пакетного внесения изменений и добавления их в DOM за одну операцию. Такой подход минимизирует перекомпоновки и перерисовки, значительно повышая производительность.
Пример использования фрагментов документа
<!DOCTYPE html>
<html>
<head>
<title>Document Fragments</title>
</head>
<body>
<div id="list"></div>
<button id="populate">Populate List</button>
<script>
document.getElementById('populate').addEventListener('click', (event) => {
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 25; i++) {
const item = document.createElement('div');
item.textContent = `Item ${i}`;
fragment.appendChild(item);
}
document.getElementById('list').appendChild(fragment);
event.target.disabled = true; // Disable the button
});
</script>
</body>
</html>В этом примере создаётся 25 элементов div, которые добавляются во фрагмент документа. Только после добавления всех элементов во фрагмент он присоединяется к DOM за одну операцию. Такой подход минимизирует перекомпоновки и перерисовки, обновляя DOM только один раз.
Клонирование узлов
Метод cloneNode()
Метод cloneNode() используется для создания копии узла. Он может клонировать как сам узел, так и узел вместе с его дочерними элементами.
Клонирование узла без дочерних элементов
<!DOCTYPE html>
<html>
<head>
<title>Cloning Nodes</title>
</head>
<body>
<div id="original">Original Node</div>
<button id="clone">Clone Node</button>
<script>
document.getElementById('clone').addEventListener('click', () => {
const original = document.getElementById('original');
const clone = original.cloneNode(false); // Clone without children
clone.id = 'clone';
clone.textContent = 'Cloned Node';
document.body.appendChild(clone);
});
</script>
</body>
</html>Этот пример демонстрирует клонирование узла без его дочерних элементов с помощью метода cloneNode(false). Клонированный узел скопирует атрибуты и текстовое содержимое исходного узла, но не его дочерние узлы.
Клонирование узла с дочерними элементами
<!DOCTYPE html>
<html>
<head>
<title>Cloning Nodes with Children</title>
</head>
<body>
<div id="original">
Original Node
<span>Child Node</span>
</div>
<button id="clone">Clone Node with Children</button>
<script>
document.getElementById('clone').addEventListener('click', () => {
const original = document.getElementById('original');
const clone = original.cloneNode(true); // Clone with children
clone.id = 'clone';
document.body.appendChild(clone);
});
</script>
</body>
</html>Этот пример демонстрирует клонирование узла вместе с его дочерними элементами с помощью метода cloneNode(true). Клонированный узел будет включать содержимое исходного узла и все его дочерние узлы.
Заключение
Эффективная манипуляция DOM критически важна для создания производительных веб-приложений. Начните с освоения основных методов создания, вставки, замены и удаления элементов, затем добавьте оптимизации: объединяйте обновления, предпочитайте CSS-классы встроенным стилям, используйте DocumentFragment для массовых вставок и клонируйте узлы при необходимости повторяющихся структур. Совместное применение этих техник обеспечивает отзывчивость страниц даже по мере роста DOM.
Связанные темы
- Манипуляция DOM — подробное руководство по чтению и изменению дерева документа.
- Обход DOM — навигация между родительскими, дочерними и соседними элементами.
- Оптимизация производительности DOM — углублённое изучение ускорения манипуляций.
- Обработка событий в DOM — эффективная настройка взаимодействий.