# Добавление и удаление узлов Изменение 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
    1. 0
    2. 1
    3. 2
    ```
`parentElem.insertBefore(elem, nextSibling)`
Вставляет `elem` в коллекцию детей `parentElem`, перед элементом `nextSibling`. Следующий код вставляет новый элемент перед вторым `
  • `: ```html
    1. 0
    2. 1
    3. 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); ```
    1. Для вставки нам нужен будущий родитель. Мы, возможно, не знаем, где точно находится `div` (или не хотим зависеть от того, где он), но если нужно вставить рядом с `div`, то родителем определённо будет `div.parentNode`.
    2. Мы хотели бы вставить *после* `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`. ## Итого Методы для создания узлов: Вставка и удаление узлов: Все эти методы возвращают `elem`. Методы для изменения DOM также описаны в спецификации DOM Level 1.