Создание и добавление элементов DOM

На этом занятии мы поговорим о том как можно добавлять и удалять элементы в DOM-дереве, то есть, фактически в HTML-документе. И начнем с такого примера. Предположим, мы хотим создать вот такое сообщение:

<!DOCTYPE html>
<html>
<head>
    <title>Уроки по JavaScript</title>
</head>
<style>
.msg {
    width: 400px;
    margin: 0 auto;
    text-align: center;
    background: #20454B;
    padding: 5px;
    border-radius: 5px;
    color: #eee;
    font-size: 24px;
}
</style>
<body>
<div class="msg">Важная информация!</div>
<script>
</script>
</body>
</html>

Но будем это делать посредством JavaScript. Поэтому тег div уберем. Теперь, чтобы создать элемент div, воспользуемся методом

document.createElement(tag)

и пропишем его так:

let div = document.createElement('div');

Далее, укажем класс стилей и содержимое:

div.className = "msg";
div.innerHTML = "Важная информация!";

Все, теперь переменная div является объектом для представления тега div с классом msg и строкой «Важная информация!». Но этот объект пока еще не является частью DOM-дерева, а значит, не содержится в HTML-документе. Если мы сейчас обновим страницу, то ничего на ней не увидим.

Для размещения объекта на HTML-странице существует несколько методов. Сейчас мы воспользуемся методом append, который может быть вызван из любого узла DOM-дерева для добавления объекта как дочернего в конец списка. Например, добавим наш div в раздел body:

document.body.append(div);

И если теперь посмотрим структуру HTML-документа, то увидим, что div идет последним в списке дочерних элементов раздела body.

Кроме метода append существуют такие методы вставки:

  • node.prepend(...nodes or strings) – вставляет узлы или строки в начало node;
  • node.before(...nodes or strings) – вставляет узлы или строки до node;
  • node.after(...nodes or strings) – вставляет узлы или строки после node;
  • node.replaceWith(...nodes or strings) – заменяет node заданными узлами или строками.

Обратите внимание, что эти методы могут вставлять не только объекты-теги, но и текстовые элементы как строки. Чтобы все было понятнее, рассмотрим работу этих методов на таком примере. Пусть у нас имеется документ:

<h1>Солнечная система</h1>
<ul>
<li>Солнце
<li>Меркурий
<li>Венера
<li>Земля
</ul>

И выполним следующие операции:

let list = document.querySelector("ul");
list.before('before');
list.after('after');

В результате до и после списка вставятся текстовые элементы со значениями before и after. Далее:

let li_1 = document.createElement('li');
li_1.innerHTML = "первый элемент";
list.prepend(li_1);
let li_2 = document.createElement('li');
li_2.innerHTML = "последний элемент";
list.append(li_2);

будет добавлен первый и последний элемент в список. И, наконец, вот такая операция:

list.replaceWith(document.createElement("hr"),
                       "замена",
                       document.createElement("br"));

заменяет весь список новой информацией: тегом hr, строкой «замена» и тегом br.

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

Методы append/prepend/before/after позволяют также перемещать уже существующие элементы. То есть, когда мы вставляем имеющийся элемент, он автоматически удаляется со своего прежнего места и добавляется в новое. Например, у нас есть исходный список и мы хотим «солнце» поместить после «земли». Это можно сделать так:

let li = document.querySelector("ul.list > li:first-child");
let list = document.querySelector("ul.list");
list.append(li);

В JavaScript помимо создания элементов-тегов с помощью метода createElement можно создавать и текстовые элементы. Для этого следует использовать метод

document.createTextNode(text)

например, так:

let textNode = document.createTextNode('Текстовый элемент');
document.body.append(textNode);

В результате мы получим простой текст внутри HTML-документа.

insertAdjacentHTML

Рассмотренные выше методы вставки добавляют либо отдельные теги, либо текстовую информацию. Причем, текст вставляется именно как текст, то есть, теги внутри текста будут отображены как текст. Например:

document.body.append("Текст с тегом");

Но, что если мы хотим вставить строку как HTML-строку с обработкой всех тегов, а не как обычный текст? Как раз для этого используется метод insertAdjacentHTML, который имеет следующий синтаксис:

elem.insertAdjacentHTML(where, html)

здесь where может принимать значения:

  • "beforebegin" – для вставки html непосредственно перед elem;
  • "afterbegin" – для вставки html как первого дочернего элемента в elem;
  • "beforeend" – для вставки html как последнего дочернего элемента в elem;
  • "afterend" – для вставки html непосредственно после elem.

Для примера возьмем вот такой список:

<ul>
<li>Меркурий
<li>Венера
<li>Земля
</ul>

И сделаем такие вставки:

let list = document.querySelector("ul.list");
list.insertAdjacentHTML("beforebegin", "Список планет<hr>");
list.insertAdjacentHTML("afterend", "<hr>Конец списка");
list.insertAdjacentHTML("afterbegin", "<li>Солнце");
list.insertAdjacentHTML("beforeend", "<li>Марс");

Смотрите как разместились эти элементы.

Существует еще два похожих метода, но более специализированных:

  • insertAdjacentText(where, text) – для вставки строки текста text;
  • insertAdjacentElement(where, elem) – для вставки элемента elem.

Например:

list.insertAdjacentText("beforebegin", "Список планет<hr>");

вставит текст как строку, а

let li = document.createElement("li");
li.innerHTML="Солнце";
list.insertAdjacentElement("afterbegin", li);

добавит соответствующий элемент.

Однако, на практике эти два метода почти не применяются. Вместо них удобнее использовать методы append/prepend/before/after просто потому, что легче писать. Если нам нужно удалить какой-либо узел из DOM-дерева, то для этого часто используется метод

node.remove()

Например, имеется список:

<ul class="list">
<li>Солнце
<li>Меркурий
<li>Венера
<li>Земля
<li>Марс
</ul>

И мы из него хотим из него удалять последние пункты, пока полностью не очистим. Это можно сделать так:

let idRemove = setInterval(function() {
    let li = document.querySelector("ul.list > li:last-child");
    if(li === null) {
       clearInterval(idRemove);
       alert("Список удален");
    }
    else li.remove();
}, 500);

cloneNode

Следующий метод cloneNode позволяет создавать клон узла DOM-дерева. Он имеет следующий синтаксис:

elem.cloneNode(flDeep);

Если flDeep равен true, то создается «глубокий» клон объекта со всеми его свойствами и дочерними элементами. При значении false получаем клон без дочерних элементов.

Когда может пригодиться такая операция. Например, у нас есть вот такая таблица:

<table border=1 cellpadding="10">
<tr><td>Солнце</td><td>Звезда</td></tr>
<tr><td>Меркурий</td><td>Планета</td></tr>
</table>

Мы хотим добавить в нее еще одну строку с планетой Венерой. Это можно сделать путем клонирования существующей строки и добавления клона в конец таблицы:

let t = document.querySelector("table");
let r = document.querySelector("table>tbody>tr:last-child");
let row = r.cloneNode(true);
row.firstChild.innerHTML = "Венера"; 
t.append(row);

Все, теперь у нас появилась еще одна строчка с Венерой.

DocumentFragment

В JavaScript есть DOM-объект специального вида – DocumentFragment. Он как бы образует фрагмент документа со своим DOM-деревом, то есть, со своим содержимым. Затем, это содержимое можно целиком вставить в любое место HTML-документа. Приведем такой простой пример. Предположим, имеется пустой список:

<ul></ul>

И мы хотим в него вставить заготовленный фрагмент списка. Это может выглядеть так:

let fr = new DocumentFragment();
let list=["Меркурий", "Венера", "Земля", "Марс"];
for(let p of list) {
    let item = document.createElement('li');
    item.innerHTML = p;
    fr.append(item);
}
 
let ul = document.querySelector("ul");
ul.append(fr);

Но это лишь пример использования DocumentFragment. В данном конкретном случае все можно реализовать проще:

let list=["Меркурий", "Венера", "Земля", "Марс"];
let ul = document.querySelector("ul");
for(let p of list) {
    let item = document.createElement('li');
    item.innerHTML = p;
    ul.append(item);
}

Вообще DocumentFragment используется крайне редко. Я здесь рассказал о нем, скорее, для целостности изложения материала. Хотя в ряде случаев он незаменим, например, при создании шаблонов документов. Но это выходит за рамки данного занятия.

Также приведу список некоторых устаревших методов вставки и удаления элементов, так как вы можете их встретить в старых скриптах и знали бы, что это такое:

  • parent.appendChild(node) – добавляет элемент в конец списка дочерних элементов;
  • parent.insertBefore(node, nextSibling) – вставляет элемент перед nextSibling;
  • parent.removeChild(node) – удаляет элемент node (здесь parent является родителем node);
  • parent.replaceChild(newElem, node) – заменяет дочерний элемент node на новый newElem.

Все эти методы работают вполне очевидным образом.

document.write

Это довольно древний метод, который появился в JavaScript когда даже не было понятия DOM-объектов и DOM-дерева. Данный метод позволяет добавлять HTML-содержимое в то место, где записан и имеет такой синтаксис:

document.write(html);

Причем, срабатывает этот метод только в момент загрузки страницы. Если вызвать его после загрузки, то все содержимое HTML-документа будет удалено!

В качестве примера приведу такую страницу:

<h1>Солнечная система</h1>
<script>
document.write('<p>Строка вставленная методом write');
</script>
<ul>
<li>Солнце
<li>Меркурий
<li>Венера
<li>Земля
</ul>

Если же мы выполним этот метод после полной загрузки, то получим следующее:

setTimeout(function() {
    document.write('Строка вставленная методом write');
}, 500);

Видите? Все содержимое HTML-документа пропало и осталась только эта одна строка.

Но, несмотря на все эти особенности, метод document.write иногда применяют для динамического формирования HTML-страницы. Дело в том, что он срабатывает еще до построения DOM-дерева и все что он добавит в документ воспринимается браузером так, словно такая страница изначально была получена с сервера. И, в частности, так можно динамически добавлять теги <script> с соответствующим содержимым и они будут исполняться после загрузки документа. Это существенный плюс данного метода в сравнении с другими рассмотренными на этом занятии. Но, все-таки, он все реже применяется и постепенно уходит в небытие.

Итак, по итогам этого занятия вы должны знать следующие методы:

Методы создания узлов

document.createElement(tag)

создает указанный объект-тег

document.createTextNode(value)

создает текстовый узел

elem.cloneNode(deep)

создает клон элемента

Методы вставки и удаления узлов

elem.append(node [или text])

добавляет node (или текст) как последний дочерний элемент

elem.prepend(node [или text])

добавляет node (или текст) как первый дочерний элемент

elem.before(node [или text])

добавляет node (или текст) непосредственно перед объектом elem

elem.after(node [или text])

добавляет node (или текст) непосредственно после объекта elem

elem.replaceWith(node [или text])

заменяет объект elem другим объектом или текстовым элементом

node.remove()

удаляет элемент

Также желательно знать и устаревшие методы, так как они могут встретиться в сторонних скриптах:

Методы вставки и удаления узлов

parent.appendChild(node)

добавляет node как последний дочерний элемент у parent

parent.insertBefore(node, nextSibling)

вставляет элемент перед nextSibling

parent.removeChild(node)

удаляет элемент node (здесь parent является родителем node)

parent.replaceChild(newElem, node)

заменяет дочерний элемент node на новый newElem

elem.insertAdjacentHTML(where, html)

вставляет фрагмент html в зависимости от значения where

Отдельный метод вставки в момент загрузки

document.write(html)

вставка фрагмента html в момент загрузки HTML-страницы

Видео по теме