Создание и удаление узлов
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">
Copy1.2. Добавление
Чтобы созданный элемент был отображен на странице, его необходимо добавить к уже существующему элементу в DOM. Допустим, что добавляем в некий элемент parentElem, для этого есть методы.
Добавляет elem в конец дочерних элементов parentElem.
Добавляет elem в коллекцию детей parentElem, перед элементом nextSibling. Если вторым аргументом указать null, тогда insertBefore сработает как appendChild.
Если элемент для вставки - это существующий узел, то он изымается из своего старого места и ставится на новое. Отсюда вытекает правило — один и тот же узел не может быть одновременно в двух местах.
1.2.1. Методы append/prepend, before/after, replaceWith
Есть методы, которые позволяют вставить что угодно и куда угодно. Во всех этих методах, nodes – DOM-узлы или строки, в любом сочетании и количестве. Причём строки вставляются как текстовые узлы.
elem.append(nodes)- добавляетnodesв конецelemelem.prepend(nodes)- добавляетnodesв началоelemelem.after(nodes)- добавляетnodesпосле узлаelemelem.before(nodes)- добавляетnodesперед узломelemelem.replaceWith(nodes)- добавляетnodesвместоelem
1.3. Удаление
Для того, чтобы удалить узел существуют два метода. Первый, более старый метод, работающий во всех браузерах, позволяет удалить ребенка elem из родителя parent. В таком случае необходимо иметь ссылку как на родителя, так и на ребенка.
Более современный метод, но с гарантированной поддержкой только в новых браузерах, он вызывается на самом элементе elem, который необходимо удалить.
1.4. Клонирование
Представим, что у нас есть элемент с текстом, и мы хотим вставить такой же элемент в другую часть документа. Мы уже знаем, что каждый элемент может существовать в document в одном экземпляре. Но элемент можно клонировать и работать с этим клоном (точной копией).
Так же мы могли бы создать новый элемент и работать с ним, но в ряде случаев гораздо эффективнее клонировать существующий, а потом изменить текст внутри. В частности, если элемент большой, то клонировать его будет быстрее, чем пересоздавать.
Создаст глубокую копию элемента – вместе с атрибутами, включая все поддерево. Если же вызвать с аргументом false, то копия будет сделана без дочерних элементов.
2. Свойство innerHTML
Еще один способ создать DOM-элементы и поместить их в дерево - это использовать строки и позволить браузеру сделать всю тяжелую работу. Как мы увидим далее, у такого подхода есть свои плюсы и минусы.
2.1. Создание узлов
elem.innerHTML — свойство, позволяет получить содержимое элемента, включая теги, в виде строки. Значение, возвращаемое innerHTML — всегда валидный HTML-код.
Оно доступно как для чтения, так и для записи. Если записать в innerHTML элемента строку с HTML-тегами, то браузер ее распарсит и превратит их в валидные DOM-узлы.
Такой код говорит браузеру распарсить строку, проверить на наличие тегов, если нашел таковые, то создать DOM-элементы и вставить их в правильном порядке. При таком подходе, в отличии от createElement, мы не получаем ссылку на созданный DOM-элемент. В то же время создавать много разметки проще.
Изменение innerHTML полностью удалит и пересоздаст всех потомков контейнера. В результате мы получаем дополнительные затраты на сериализацию уже существующей разметки, что не очень хорошо.
3. Метод insertAdjacentHTML()
Метод парсит указанную строку как HTML и добавляет результирующие узлы в указанное место DOM-дерева. Не делает повторный рендеринг для существующих элементов внутри элемента-родителя на котором используется. Это позволяет избежать дополнительного этапа сериализации, делая его быстрее, чем непосредственная манипуляция innerHTML.
position — позиция относительно элемента. Принимает одно из следующих значений:
'beforebegin'- перед element'afterbegin'- внутрь element, в самое начало контента'beforeend'- внутрь element, в самый конец контента'afterend'- после element

beforebegin и afterend работают только в том случае, если узел уже находится в DOM-дереве и имеет родительский элемент.
У этого метода есть братья-близнецы. Их синтаксис, за исключением последнего параметра, полностью совпадает с 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