W3docs

Техники манипуляции 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 в JavaScript?
Какие из следующих методов используются для манипуляции DOM в JavaScript?
Was this page helpful?