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

1. Работа с DOM-узлами

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

1.1. Создание

document.createElement(tagName)
Copy

Создает HTML-элемент по указанному имени тега и возвращает ссылку на него как результат своего выполнения. tagName - это строка, указывающая тип создаваемого элемента. Элемент создается в памяти, в DOM его еще нет.

const heading = document.createElement('h1');
console.log(heading); // <h1></h1>

heading.textContent = 'This is a heading';
console.log(heading); // <h1>This is a heading</h1>

const image = document.createElement('img');
image.setAttribute('src', 'https://placeimg.com/640/480/nature');
image.setAttribute('alt', 'nature');

console.log(image); // <img src="https://placeimg.com/640/480/nature" alt="nature">
Copy

1.2. Добавление

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

parentElem.appendChild(elem)
Copy

Добавляет elem в конец дочерних элементов parentElem.

parentElem.insertBefore(elem, nextSibling)
Copy

Добавляет elem в коллекцию детей parentElem, перед элементом nextSibling. Если вторым аргументом указать null, тогда insertBefore сработает как appendChild.

Ссылка на пример в Codepen.io

1.2.1. Методы append/prepend, before/after, replaceWith

Есть методы, которые позволяют вставить что угодно и куда угодно. Во всех этих методах, nodes – DOM-узлы или строки, в любом сочетании и количестве. Причём строки вставляются как текстовые узлы.

  • elem.append(nodes) - добавляет nodes в конец elem

  • elem.prepend(nodes) - добавляет nodes в начало elem

  • elem.after(nodes) - добавляет nodes после узла elem

  • elem.before(nodes) - добавляет nodes перед узлом elem

  • elem.replaceWith(nodes) - добавляет nodes вместо elem

Ссылка на пример в Codepen.io

1.3. Удаление

Для того, чтобы удалить узел существуют два метода. Первый, более старый метод, работающий во всех браузерах, позволяет удалить ребенка elem из родителя parent. В таком случае необходимо иметь ссылку как на родителя, так и на ребенка.

parent.removeChild(elem)
Copy

Более современный метод, но с гарантированной поддержкой только в новых браузерах, он вызывается на самом элементе elem, который необходимо удалить.

elem.remove()
Copy

Ссылка на пример в Codepen.io

1.4. Клонирование

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

Так же мы могли бы создать новый элемент и работать с ним, но в ряде случаев гораздо эффективнее клонировать существующий, а потом изменить текст внутри. В частности, если элемент большой, то клонировать его будет быстрее, чем пересоздавать.

elem.cloneNode(true)
Copy

Создаст глубокую копию элемента – вместе с атрибутами, включая все поддерево. Если же вызвать с аргументом false, то копия будет сделана без дочерних элементов.

Ссылка на пример в Codepen.io

2. Свойство innerHTML

Еще один способ создать DOM-элементы и поместить их в дерево - это использовать строки и позволить браузеру сделать всю тяжелую работу. Как мы увидим далее, у такого подхода есть свои плюсы и минусы.

2.1. Создание узлов

elem.innerHTML — свойство, позволяет получить содержимое элемента, включая теги, в виде строки. Значение, возвращаемое innerHTML — всегда валидный HTML-код.

Оно доступно как для чтения, так и для записи. Если записать в innerHTML элемента строку с HTML-тегами, то браузер ее распарсит и превратит их в валидные DOM-узлы.

elem.innerHTML = '<p class="text">Pellentesque habitant.</p>';
Copy

Такой код говорит браузеру распарсить строку, проверить на наличие тегов, если нашел таковые, то создать DOM-элементы и вставить их в правильном порядке. При таком подходе, в отличии от createElement, мы не получаем ссылку на созданный DOM-элемент. В то же время создавать много разметки проще.

Ссылка на пример в Codepen.io

3. Метод insertAdjacentHTML()

Метод парсит указанную строку как HTML и добавляет результирующие узлы в указанное место DOM-дерева. Не делает повторный рендеринг для существующих элементов внутри элемента-родителя на котором используется. Это позволяет избежать дополнительного этапа сериализации, делая его быстрее, чем непосредственная манипуляция innerHTML.

element.insertAdjacentHTML(position, string)
Copy

position — позиция относительно элемента. Принимает одно из следующих значений:

  • 'beforebegin' - перед element

  • 'afterbegin' - внутрь element, в самое начало контента

  • 'beforeend' - внутрь element, в самый конец контента

  • 'afterend' - после element

inserAdjacentHTML

Ссылка на пример в Codepen.io

У этого метода есть братья-близнецы. Их синтаксис, за исключением последнего параметра, полностью совпадает с insertAdjacentHTML. Вместе они образуют универсальный швейцарский нож для вставки чего угодно куда угодно.

  • elem.insertAdjacentElement(position, elem) — вставляет в произвольное место не HTML-строку, а элемент elem.

  • elem.insertAdjacentText(position, text) — создаёт текстовый узел из строки text и вставляет его в указанное место относительно elem.

4. Оптимизация работы с DOM

Манипуляция DOM-дерева - это дорогая операция. Необходимо всеми возможными методами стараться минимизировать количество обращений к DOM. Разберемся с очень важными концепциями при работе c DOM-деревом.

Как браузер рисует страницу

4.1. Repaint

Происходит, когда изменения произошли в стилях элемента влияющих на внешний вид, но не на геометрию. Например opacity, background-color, visibility и outline. Браузер отрисовывает его заново, с учётом нового стиля. Это дорогая операция, потому что браузер проверяет видимость всех остальных узлов в дереве, один или более могут оказаться скрыты под изменившим внешний вид.

4.2. Reflow

Происходит, когда изменения затрагивают содержимое, структуру документа, положение элементов. Идет пересчет позиционирования и размеров всех элементов, что ведет к перерисовке части или всего документа. Изменение размера одного родительского контейнера повлияет на всех его детей и предков. Имеет значительно большее влияние на производительность, чем repaint.

4.3. Выводы

Всегда необходимо думать об оптимизации работы с DOM. Все вышеперечисленные операции блокируют браузер. Страница не может выполнять никакие другие операции в то время, когда происходит reflow или repaint. Причинами таких изменений обычно являются:

  • Манипуляции с DOM (добавление, удаление, изменение, перестановка элементов)

  • Изменение содержимого, в т.ч. текста в полях форм

  • Расчёт или изменение CSS-свойств

  • Добавление и удаление таблиц стилей

  • Манипуляции с атрибутом class

  • Манипуляции с окном браузера (изменения размеров, прокрутка)

  • Активация псевдоклассов (например :hover)

4.4. Дополнительные материалы

Last updated