# Добавление и удаление узлов
Изменение DOM -- ключ к созданию "живых" страниц.
В этой главе мы рассмотрим, как создавать новые элементы "на лету" и заполнять их данными.
[cut]
## Пример: показ сообщения
В качестве примера рассмотрим добавление сообщения на страницу, чтобы оно было оформленно красивее чем обычный `alert`.
HTML-код для сообщения:
```html
*!*
Ура! Вы прочитали это важное сообщение.
*/!*
```
## Создание элемента
Для создания элементов используются следующие методы:
- `document.createElement(tag)`
- Создает новый элемент с указанным тегом:
```js
var div = document.createElement('div');
```
- `document.createTextNode(text)`
- Создает новый *текстовый* узел с данным текстом:
```js
var textElem = document.createTextNode('Тут был я');
```
### Создание сообщения
В нашем случае мы хотим сделать DOM-элемент `div`, дать ему классы и заполнить текстом:
```js
var div = document.createElement('div');
div.className = "alert alert-success";
div.innerHTML = "Ура! Вы прочитали это важное сообщение.";
```
После этого кода у нас есть готовый DOM-элемент. Пока что он присвоен в переменную `div`, но не виден, так как никак не связан со страницей.
## Добавление элемента: appendChild, insertBefore
Чтобы DOM-узел был показан на странице, его необходимо вставить в `document`.
Для этого первым делом нужно решить, куда мы будем его вставлять. Предположим, что мы решили, что вставлять будем в некий элемент `parentElem`, например `var parentElem = document.body`.
Для вставки внутрь `parentElem` есть следующие методы:
- `parentElem.appendChild(elem)`
- Добавляет `elem` в конец дочерних элементов `parentElem`.
Следующий пример добавляет новый элемент в конец `
`:
```html
- 0
- 1
- 2
```
- `parentElem.insertBefore(elem, nextSibling)`
- Вставляет `elem` в коллекцию детей `parentElem`, перед элементом `nextSibling`.
Следующий код вставляет новый элемент перед вторым `
- `:
```html
- 0
- 1
- 2
```
Для вставки элемента в начало достаточно указать, что вставлять будем перед первым потомком:
```js
list.insertBefore(newLi, list.firstChild);
```
У читателя, который посмотрит на этот код внимательно, наверняка возникнет вопрос: "А что, если `list` вообще пустой, в этом случае ведь `list.firstChild = null`, произойдёт ли вставка?"
Ответ -- да, произойдёт.
**Дело в том, что если вторым аргументом указать `null`, то `insertBefore` сработает как `appendChild`:**
```js
parentElem.insertBefore(elem, null);
// то же, что и:
parentElem.appendChild(elem)
```
Так что `insertBefore` универсален.
[smart]
Все методы вставки возвращают вставленный узел.
Например, `parentElem.appendChild(elem)` возвращает `elem`.
[/smart]
### Пример использования
Добавим сообщение в конец ``:
```html
Моя страница
```
...А теперь -- в начало ``:
```html
Моя страница
```
## Клонирование узлов: cloneNode
А как бы вставить второе похожее сообщение?
Конечно, можно сделать функцию для генерации сообщений и поместить туда этот код, но в ряде случаев гораздо эффективнее -- *клонировать* существующий `div`, а потом изменить текст внутри. В частности, если элемент большой, то клонировать его будет гораздо быстрее, чем пересоздавать.
Вызов `elem.cloneNode(true)` создаст "глубокую" копию элемента -- вместе с атрибутами, включая подэлементы. Если же вызвать с аргумнтом `false`, то он копия будет без подэлементов, но это нужно гораздо реже.
### Копия сообщения
Пример со вставкой копии сообщения:
```html
Моя страница
```
Обратите внимание на последнюю строку, которая вставляет `div2` после `div`:
```js
div.parentNode.insertBefore(div2, div.nextSibling);
```
- Для вставки нам нужен будущий родитель. Мы, возможно, не знаем, где точно находится `div` (или не хотим зависеть от того, где он), но если нужно вставить рядом с `div`, то родителем определённо будет `div.parentNode`.
- Мы хотели бы вставить *после* `div`, но метода `insertAfter` нет, есть только `insertBefore`, поэтому вставляем *перед* его правым соседом `div.nextSibling`.
## Удаление узлов: removeChild
Для удаления узла есть два метода:
- `parentElem.removeChild(elem)`
- Удаляет `elem` из списка детей `parentElem`.
- `parentElem.replaceChild(newElem, elem)`
- Среди детей `parentElem` удаляет `elem` и вставляет на его место `newElem`.
Оба этих метода возвращают удаленный узел, то есть `elem`. Если нужно, его можно вставить в другое место DOM тут же или в будущем.
[smart]
Если вы хотите *переместить* элемент на новое место -- не нужно его удалять со старого.
**Все методы вставки автоматически удаляют вставляемый элемент со старого места.**
Конечно же, это очень удобно.
Например, поменяем элементы местами:
```html
Первый
Второй
```
[/smart]
[smart header="Метод `remove`"]
В современном стандарте есть также метод [elem.remove()](https://dom.spec.whatwg.org/#dom-childnode-remove), который удаляет элемент напрямую, не требуя ссылки на родителя. Это зачастую удобнее, чем `removeChild`.
Он поддерживается во всех современных браузерах, кроме IE11-. Впрочем, легко подключить или даже сделать полифилл.
[/smart]
### Удаление сообщения
Сделаем так, что через секунду сообщение пропадёт:
```html
Сообщение пропадёт через секунду
```
## Текстовые узлы для вставки текста
При работе с сообщением мы использовали только узлы-элементы и `innerHTML`.
Но и текстовые узлы тоже имеют интересную область применения!
Если текст для сообщения нужно показать именно как текст, а не как HTML, то можно обернуть его в текстовый узел.
Например:
```html
```
В современных браузерах (кроме IE8-) в качестве альтернативы можно использовать присвоение `textContent`.
## Итого
Методы для создания узлов:
- `document.createElement(tag)` -- создает элемент
- `document.createTextNode(value)` -- создает текстовый узел
- `elem.cloneNode(deep)` -- клонирует элемент, если `deep == true`, то со всеми потомками, если `false` -- без потомков.
Вставка и удаление узлов:
- `parent.appendChild(elem)`
- `parent.insertBefore(elem, nextSibling)`
- `parent.removeChild(elem)`
- `parent.replaceChild(newElem, elem)`
Все эти методы возвращают `elem`.
Методы для изменения DOM также описаны в спецификации DOM Level 1.