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

## 1. Работа с DOM-узлами <a href="#rabota-s-dom-uzlami" id="rabota-s-dom-uzlami"></a>

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

### 1.1. Создание <a href="#sozdanie" id="sozdanie"></a>

```bash
document.createElement(tagName)
Copy
```

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

```js
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. Добавление <a href="#dobavlenie" id="dobavlenie"></a>

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

```bash
parentElem.appendChild(elem)
Copy
```

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

```bash
parentElem.insertBefore(elem, nextSibling)
Copy
```

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

[Ссылка на пример в Codepen.io](https://codepen.io/goit-fe-adv/pen/zdgLmN/)

{% hint style="success" %}
<mark style="color:green;">Если элемент для вставки - это существующий узел, то он изымается из своего старого места и ставится на новое. Отсюда вытекает правило — один и тот же узел не может быть одновременно в двух местах.</mark>
{% endhint %}

#### 1.2.1. Методы append/prepend, before/after, replaceWith <a href="#metody-appendprepend-beforeafter-replacewith" id="metody-appendprepend-beforeafter-replacewith"></a>

Есть методы, которые позволяют вставить что угодно и куда угодно. Во всех этих методах, `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](https://codepen.io/goit-fe-adv/pen/VzoEXa/)

### 1.3. Удаление <a href="#udalenie" id="udalenie"></a>

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

```bash
parent.removeChild(elem)
Copy
```

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

```bash
elem.remove()
Copy
```

[Ссылка на пример в Codepen.io](https://codepen.io/goit-fe-adv/pen/MvNBde/)

### 1.4. Клонирование <a href="#klonirovanie" id="klonirovanie"></a>

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

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

```bash
elem.cloneNode(true)
Copy
```

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

[Ссылка на пример в Codepen.io](https://codepen.io/goit-fe-adv/pen/JygaNZ/)

## 2. Свойство innerHTML <a href="#svoistvo-innerhtml" id="svoistvo-innerhtml"></a>

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

### 2.1. Создание узлов <a href="#sozdanie-uzlov" id="sozdanie-uzlov"></a>

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

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

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

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

{% hint style="success" %}
<mark style="color:green;">Изменение</mark> <mark style="color:green;"></mark><mark style="color:green;">`innerHTML`</mark> <mark style="color:green;"></mark><mark style="color:green;">полностью удалит и пересоздаст всех потомков контейнера. В результате мы получаем дополнительные затраты на сериализацию уже существующей разметки, что не очень хорошо.</mark>
{% endhint %}

[Ссылка на пример в Codepen.io](https://codepen.io/goit-fe-adv/pen/QMeVyj/)

## 3. Метод insertAdjacentHTML() <a href="#metod-insertadjacenthtml" id="metod-insertadjacenthtml"></a>

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

```bash
element.insertAdjacentHTML(position, string)
Copy
```

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

* `'beforebegin'` - перед element
* `'afterbegin'` - внутрь element, в самое начало контента
* `'beforeend'` - внутрь element, в самый конец контента
* `'afterend'` - после element

![inserAdjacentHTML](https://goit.global/javascript/ru/v1/module-07/images/insertadjacent.png)

{% hint style="success" %}
<mark style="color:green;">`beforebegin`</mark> <mark style="color:green;"></mark><mark style="color:green;">и</mark> <mark style="color:green;"></mark><mark style="color:green;">`afterend`</mark> <mark style="color:green;"></mark><mark style="color:green;">работают только в том случае, если узел уже находится в DOM-дереве и имеет родительский элемент.</mark>
{% endhint %}

[Ссылка на пример в Codepen.io](https://codepen.io/goit-fe-adv/pen/jLgQyR/)

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

* `elem.insertAdjacentElement(position, elem)` — вставляет в произвольное место не HTML-строку, а элемент `elem`.
* `elem.insertAdjacentText(position, text)` — создаёт текстовый узел из строки `text` и вставляет его в указанное место относительно `elem`.

## 4. Оптимизация работы с DOM <a href="#optimizaciya-raboty-s-dom" id="optimizaciya-raboty-s-dom"></a>

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

[Как браузер рисует страницу](https://youtu.be/dndeRnzkJDU)

### 4.1. Repaint <a href="#repaint" id="repaint"></a>

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

### 4.2. Reflow <a href="#reflow" id="reflow"></a>

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

### 4.3. Выводы <a href="#vyvody" id="vyvody"></a>

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

* Манипуляции с DOM (добавление, удаление, изменение, перестановка элементов)
* Изменение содержимого, в т.ч. текста в полях форм
* Расчёт или изменение CSS-свойств
* Добавление и удаление таблиц стилей
* Манипуляции с атрибутом `class`
* Манипуляции с окном браузера (изменения размеров, прокрутка)
* Активация псевдоклассов (например `:hover`)

{% hint style="success" %}
<mark style="color:green;">Современные браузеры оптимизируют процесс отрисовки страницы без вмешательства разработчика. И все же не забывайте думать о производительности, следуйте лучшим практикам из статей в дополнительных материалах этой секции.</mark>
{% endhint %}

### 4.4. Дополнительные материалы <a href="#dopolnitelnye-materialy" id="dopolnitelnye-materialy"></a>

* [Рендеринг WEB-страницы: что об этом должен знать front-end разработчик](https://habr.com/post/224187/)
* [Understanding Repaint and Reflow in JavaScript](https://blog.usejournal.com/what-the-heck-is-repaint-and-reflow-in-the-browser-b2d0fb980c08)
* [Avoid Large, Complex Layouts and Layout Thrashing](https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing)
* [10 Ways to Minimize Reflows and Improve Performance](https://www.sitepoint.com/10-ways-minimize-reflows-improve-performance/)
