W3docs

Узлы DOM в JavaScript

Узлы DOM — основа Document Object Model. Узнайте, как создавать, добавлять, удалять и заменять узлы с помощью JavaScript.

Понимание узлов DOM

Document Object Model (DOM) — фундаментальная концепция в веб-разработке, выступающая интерфейсом между документами HTML и JavaScript. Когда веб-страница загружается, браузер разбирает HTML и строит DOM — живое дерево объектов в памяти, которое JavaScript может читать и изменять. Каждое изменение, вносимое в DOM, мгновенно отражается на странице, именно это делает возможными интерактивные веб-приложения.

В этом руководстве рассматриваются узлы DOM: что они собой представляют, какие типы существуют, как их определять, а также как создавать, добавлять, удалять и заменять их. После изучения вы поймёте строительные блоки, на которых основаны все высокоуровневые техники работы с DOM — например, выбор элементов и обход дерева.

Что такое узел DOM?

Узел — это базовая единица дерева DOM. Всё в документе — элементы, текст внутри них, атрибуты и даже комментарии — представлено в виде узла. Каждый узел является объектом, наследующим от встроенного класса Node, поэтому все они имеют общий набор свойств (nodeType, nodeName, parentNode, childNodes и т. д.), используемых для навигации и манипуляции.

Удобная аналогия: HTML, который вы пишете, — это рецепт, а DOM — это живой объект, который браузер готовит по этому рецепту. После загрузки страницы вы больше не редактируете HTML-текст — вы редактируете узлы.

<!DOCTYPE html>
<html>
<head>
    <title>DOM Nodes Example</title>
</head>
<body>
    <div id="example">
        <!-- This is a comment node -->
        <p>This is a text node within an element node.</p>
    </div>
    <script>
        let exampleDiv = document.getElementById('example');
        exampleDiv.style.border = '2px solid blue'; // Highlight the div element
        exampleDiv.style.padding = '10px';
    </script>
</body>
</html>

Пояснение: В этом примере мы обращаемся к div с идентификатором example и применяем CSS-стили, чтобы визуально выделить его. Это демонстрирует, как напрямую управлять свойствами стиля узла-элемента, делая его более заметным на странице.

Различные типы узлов DOM

Каждый узел содержит числовое свойство nodeType, указывающее тип узла. Наиболее распространённые типы, с которыми вы будете работать:

nodeTypeКонстантаЗначение
1Node.ELEMENT_NODEHTML-элемент, например <div> или <p>
3Node.TEXT_NODEТекст внутри элемента (включая пробельные символы)
8Node.COMMENT_NODEHTML-комментарий (<!-- ... -->)
9Node.DOCUMENT_NODEСам объект document, корень дерева

Распространённый сюрприз для новичков: пробельные символы считаются текстовыми узлами. Переносы строк и отступы между тегами создают пустые текстовые узлы, поэтому childNodes нередко возвращает больше элементов, чем ожидается. (Если вам нужны только дочерние элементы без пробельных текстовых узлов, используйте children вместо childNodes.) Атрибуты тоже являются узлами, однако в современном коде их читают с помощью getAttribute() / setAttribute(), а не обходом узлов атрибутов напрямую.

Пример ниже перебирает дочерние узлы <div> и выводит тип каждого из них:

<!DOCTYPE html>
<html>
<head>
    <title>Node Types Interactive Example</title>
    <style>
        #info {
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
        #output {
            border: 1px solid #ccc;
            padding: 10px;
        }
        button {
            cursor: pointer;
            margin-bottom: 10px;
        }
    </style>
</head>
<body>
    <div id="info">
        <!-- This is a comment node -->
        <p>Element node with a <span>child element node</span> and some text.</p>
    </div>
    <button onclick="displayNodeTypes()">Show Node Types</button>
    <div id="output">Node types will be displayed here.</div>
    <script>
        function displayNodeTypes() {
            const infoDiv = document.getElementById('info');
            const outputDiv = document.getElementById('output');
            outputDiv.innerHTML = '';  // Clear previous output
            const nodeTypes = [];

            // Iterate over all child nodes of the div
            infoDiv.childNodes.forEach(node => {
                let typeDescription = '';
                switch(node.nodeType) {
                    case Node.ELEMENT_NODE:
                        typeDescription = 'Element node: ' + node.tagName;
                        break;
                    case Node.TEXT_NODE:
                        // Trim text content and check if it's not empty
                        let textContent = node.textContent.trim();
                        if (textContent) {
                            typeDescription = 'Text node: "' + textContent + '"';
                        }
                        break;
                    case Node.COMMENT_NODE:
                        typeDescription = 'Comment node: "' + node.textContent.trim() + '"';
                        break;
                }

                // Only add non-empty descriptions
                if (typeDescription) {
                    nodeTypes.push(typeDescription);
                    const p = document.createElement('p');
                    p.textContent = typeDescription;
                    outputDiv.appendChild(p);
                }
            });

            // If no nodes are visible or found
            if (nodeTypes.length === 0) {
                outputDiv.textContent = 'No visible nodes found.';
            }
        }
    </script>
</body>
</html>

HTML-документ демонстрирует, как определять и отображать различные типы узлов DOM (например, узлы-элементы, текстовые узлы и узлы-комментарии). Он включает стилизованный блок для отображения этих узлов, кнопку, запускающую JavaScript-функцию для анализа и вывода списка узлов, и область для отображения результатов. JavaScript-функция проверяет каждый узел в заданной части документа, классифицирует его и выводит его тип и данные.

Управление узлами DOM

Интерактивные страницы создаются путём изменения узлов после загрузки страницы. Четыре операции, к которым вы будете обращаться чаще всего: создание, добавление, удаление и замена узлов. Более широкий обзор техник, основанных на этих операциях, см. в разделе Манипуляции с DOM.

Создание и добавление узлов

Создание узла и его добавление — два отдельных шага. document.createElement() создаёт отдельный узел, ещё не размещённый на странице; appendChild() (или append(), prepend(), insertBefore()) — это то, что фактически вставляет его в дерево:

<!DOCTYPE html>
<html>
<head>
    <title>Add Node Example</title>
</head>
<body>
    <button onclick="addNewParagraph()">Add Paragraph</button>
    <script>
        function addNewParagraph() {
            let newNode = document.createElement('p');
            newNode.textContent = 'This is a new paragraph.';
            document.body.appendChild(newNode);
        }
    </script>
</body>
</html>

Пояснение: Этот пример включает кнопку, которая при нажатии запускает функцию, создающую новый абзац (<p>) и добавляющую его в тело документа. Демонстрируется, как JavaScript можно использовать для динамического добавления контента на страницу — это особенно полезно для приложений, которым необходимо обновляться в реальном времени без перезагрузки страницы.

Удаление узлов

Чтобы убрать узел со страницы, вызовите removeChild() у его родителя или — проще в современных браузерах — вызовите node.remove() непосредственно на самом узле. В примере используется removeChild(), чтобы явно показать отношение родитель/дочерний элемент:

<!DOCTYPE html>
<html>
<head>
    <title>Remove Node Example</title>
</head>
<body>
    <div id="container">
        <p id="toBeRemoved">This paragraph will be removed. <button onclick="removeParagraph()">Remove</button></p>
    </div>
    <script>
        function removeParagraph() {
            let parentNode = document.getElementById('container');
            let childNode = document.getElementById('toBeRemoved');
            parentNode.removeChild(childNode);
        }
    </script>
</body>
</html>

Пояснение: Этот скрипт представляет практическую демонстрацию удаления DOM-элемента с помощью кнопки, встроенной в сам абзац. Метод removeChild() используется для удаления указанного элемента, демонстрируя динамическое действие, инициируемое пользователем и напрямую изменяющее структуру документа.

Замена узлов

replaceChild(newNode, oldNode) заменяет один узел другим за один шаг, сохраняя позицию старого узла в дереве:

<!DOCTYPE html>
<html>
<head>
    <title>Replace Node Example</title>
</head>
<body>
    <div id="oldElement">This element will be replaced. <button onclick="replaceElement()">Replace</button></div>
    <script>
        function replaceElement() {
            let newNode = document.createElement('div');
            newNode.textContent = 'This is a replacement.';
            let oldNode = document.getElementById('oldElement');
            oldNode.parentNode.replaceChild(newNode, oldNode);
        }
    </script>
</body>
</html>

Пояснение: В этом интерактивном примере кнопка запускает замену существующего div на вновь созданный div. Это иллюстрирует метод replaceChild(), особенно полезный в ситуациях, когда элемент нужно обновить на основе действий пользователя или внешних событий, например при получении новых данных с сервера.

Лучшие практики и советы

  • Минимизируйте манипуляции с DOM: каждое изменение может вынуждать браузер пересчитывать макет (reflow) и перерисовывать страницу (repaint). Группируйте изменения вместо того, чтобы обращаться к DOM в тесном цикле.
  • Сначала читайте, потом пишите: смешивание чтения (например, offsetHeight) и записи внутри цикла вызывает повторное layout thrashing. Группируйте все чтения вместе и все записи вместе.
  • Собирайте вне экрана, вставляйте за один раз: используйте DocumentFragment (показан ниже), чтобы собрать множество элементов перед их вставкой в одной операции.
  • Кешируйте выборки: сохраняйте результаты getElementById/querySelector в переменной, а не запрашивайте один и тот же элемент повторно. Подробнее см. в разделе Оптимизация производительности DOM.
Информация

Всегда обеспечивайте кросс-браузерную совместимость при выполнении манипуляций с DOM. Тестирование в разных браузерах помогает предотвратить неожиданное поведение и гарантирует единообразный пользовательский опыт.

Пример использования DocumentFragment

DocumentFragment — это лёгкий контейнер для узлов, не привязанный к живому документу. Поскольку он не является частью живого документа, добавление к нему узлов не вызывает затрат на макетирование — браузер выполняет reflow только один раз, когда вы вставляете весь фрагмент на страницу. Это делает его идеальным для одновременного добавления большого количества элементов:

<!DOCTYPE html>
<html>
<head>
    <title>DocumentFragment Example</title>
</head>
<body>
    <div id="list-container">
        <h2>Item List</h2>
        <ul id="item-list">
            <!-- Items will be added here -->
        </ul>
        <button id="add-items">Add 100 Items</button>
    </div>

    <script>
        const itemList = document.getElementById('item-list');
        const addItemsButton = document.getElementById('add-items');

        addItemsButton.addEventListener('click', () => {
            // Create a DocumentFragment
            const fragment = document.createDocumentFragment();

            // Add 100 list items to the fragment
            for (let i = 1; i <= 100; i++) {
                const listItem = document.createElement('li');
                listItem.textContent = `Item ${i}`;
                fragment.appendChild(listItem);
            }

            // Append the fragment to the item list
            itemList.appendChild(fragment);
        });
    </script>
</body>
</html>
  • Начнём с выбора элемента <ul>, куда будут добавляться новые элементы, и кнопки, запускающей добавление.
  • При нажатии кнопки "Add 100 Items" создаётся объект DocumentFragment.
  • Затем в цикле создаётся 100 элементов <li>, им задаётся текстовое содержимое, и каждый добавляется в DocumentFragment.
  • Наконец, DocumentFragment добавляется в элемент <ul>. Поскольку фрагмент не является частью живого DOM во время фазы сборки, этот подход минимизирует reflow и repaint, повышая производительность.

Использование DocumentFragment особенно полезно, когда нужно добавить в DOM большое количество элементов за один раз, поскольку это снижает накладные расходы, связанные с многократными reflow и repaint, обеспечивая более плавные и быстрые обновления.

Узлы DOM и деревья DOM

Узлы DOM и дерево DOM — связанные понятия, однако они относятся к разным аспектам того, как структурирован и обрабатывается веб-документ:

  1. Узлы DOM: узел DOM — это отдельный компонент в рамках Document Object Model (DOM). Каждый элемент, текст, комментарий и атрибут в HTML- или XML-документе считается узлом. Узлы являются фундаментальными строительными блоками документа. Например, тег <h1>, текст внутри этого тега и даже атрибуты тега (например, class="title") — всё это отдельные узлы.
  2. Дерево DOM: дерево DOM — это вся структура или иерархия узлов в том виде, в каком они организованы в документе. Это древовидное представление, показывающее, как все узлы в документе связаны друг с другом. Узлы в этом дереве имеют отношения родитель-потомок, подобно папкам и файлам на компьютере. Например, в HTML-документе узел <body> может быть родительским узлом с несколькими дочерними узлами, такими как <div>, <p> и <img>, представляющими разные части веб-страницы.

Проще говоря, узел DOM можно представить как единственную точку или фрагмент более крупного пазла, тогда как дерево DOM представляет собой полный пазл или всю совокупность связей между этими точками. Дерево DOM удобно для навигации и управления структурой документа с помощью языков программирования, таких как JavaScript, поскольку позволяет разработчикам эффективно находить (выбирать), изменять или обновлять элементы внутри вложенной иерархии.

Заключение

Узлы — это атомы DOM. Как только вы научитесь распознавать различные типы узлов, читать nodeType узла и уверенно создавать, добавлять, удалять и заменять узлы, любая высокоуровневая задача работы с DOM станет доступной. Помните о производительности — группируйте изменения и используйте DocumentFragment при одновременной вставке большого количества элементов.

Что читать дальше

Практика

Практика
Какие из перечисленных ниже являются типами узлов DOM в веб-документе?
Какие из перечисленных ниже являются типами узлов DOM в веб-документе?
Was this page helpful?