Создание, вставка и удаление элементов в JavaScript

Содержание:
  1. Создания элементов и текстовых узлов
  2. Вставка элементов и текстовых узлов
  3. DocumentFragment
  4. Замена и клонирование узлов
  5. Удаление узлов
  6. Задачи
  7. Комментарии

На этом уроке мы научимся создавать узлы-элементы (createElement) и текстовые узлы (createTextNode). А также рассмотрим методы, предназначенные для добавления узлов к дереву (appendChild, insertBefore) и для удаления узлов из дерева (removeChild).

Создания элементов и текстовых узлов

Создание элемента в JavaScript выполняется с помощью метода createElement:

// $elem – переменная, в которую сохраним созданный элемент
const $elem = document.createElement('tag');

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

Например, создадим элемент p:

const $elem = document.createElement('p');

Создание текстового узла в JavaScript осуществляется посредством метода createTextNode:

const text = document.createTextNode('text');

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

Например, создадим текстовый узел с текстом «Я новый текстовый узел»:

const text = document.createTextNode('Я новый текстовый узел');

Вставка элементов и текстовых узлов

Чтобы созданный элемент (или текстовый узел) появился в нужном месте страницы его необходимо туда вставить.

Выполнить в JavaScript это можно посредством различных методов.

Одни из самых старых – appendChild и insertBefore.

appendChild

appendChild предназначен для вставки узла в конец элемента (т.е. после последнего его дочернего узла) для которого этот метод вызывается:

// $elem – элемент, во внутрь которого после последнего его дочернего узла необходимо вставить узел $node
$elem.appendChild($node);

В качестве результата этот метод возвращает добавленный на страницу узел.

Пример, в котором добавим новый <li> в конец <ol>:

<ol id="colors">
  <li>Красный</li>
  <li>Оранжевый</li>
  <li>Жёлтый</li>
  <li>Зелёный</li>
  <li>Голубой</li>
  <li>Синий</li>
</ol>

<script>
const $newLi = document.createElement('li');
$newLi.textContent = 'Фиолетовый';
const $colors = document.querySelector('#colors');
$colors.appendChild($newLi);
</script>

insertBefore

insertBefore предназначен для вставки узла node перед nextSibling в $elem:

$elem.insertBefore(node, nextSibling);

Если в качестве nextSibling передать null, то данный метод вставит node после последнего дочернего узла $elem. Т.е. выполнит действия аналогично appendChild.

В качестве результата метод insertBefore возвращает вставленный узел.

Например, вставим новый элемент <li> перед третьим:

<ol id="colors">
  <li>Красный</li>
  <li>Оранжевый</li>
  <li>Зелёный</li>
  <li>Голубой</li>
  <li>Синий</li>
  <li>Фиолетовый</li>
</ol>

<script>
  const $newLi = document.createElement('li');
  $newLi.textContent = 'Жёлтый';
  const $colors = document.querySelector('#colors');
  $colors.insertBefore($newLi, $colors.children[2]);
</script>

Современные методы вставки и замены

В JavaScript имеются следующие современные методы для вставки элементов и строк:

  • node.append – для добавления узлов или строк в конец node;
  • node.prepend – для вставки узлов или строк в начало node;
  • node.before – для вставки узлов или строк до node;
  • node.after – для вставки узлов или строк после node.

Пример использования методов:

<div id="message">
  <p>message...</p>
</div>

<script>
const $message = document.querySelector('#message');

// вставим строку «before» перед $message
$message.before('before');
// вставим строку «after» перед $message
$message.after('after');

const $p1 = document.createElement('p');
$p1.textContent = 'prepend';
// вставим элемент $p1 в начало $message
$message.prepend($p1);

const $p2 = document.createElement('p');
$p2.textContent = 'append';
// вставим элемент $p2 в конец $message
$message.append($p2);
</script>

В результате:

before
<div id="message">
  <p>prepend</p>
  <p>message...</p>
  <p>append</p>
</div>
after

InsertAdjacent

В JavaScript имеется набор методов insertAdjacent, которые позволяют вставить один или несколько узлов в указанную позицию position относительно $elem

.

Всего существует 3 таких метода:

  • $elem.insertAdjacentElement(position, element) – для вставки элемента (element);
  • $elem.insertAdjacentHTML(position, htmlString) – для вставки строки (htmlString) как HTML;
  • $elem.insertAdjacentText(position, string) – для вставки строки (string);

Значение position, может быть, одним из следующих:

  • 'beforebegin' – непосредственно перед $elem;
  • 'afterbegin' – перед первым дочерним узлом $elem;
  • 'beforeend' – после последнего дочернего узла $elem;
  • 'afterend' – сразу после $elem;

Пример использования insertAdjacentHTML:

<ul id="list">
  <li>CSS</li>
</ul>

<script>
  const $list = document.querySelector('#list');

  $list.insertAdjacentHTML('beforebegin', '<h2>Веб-технологии</h2>');
  $list.insertAdjacentHTML('afterbegin', '<li>HTML</li>');
  $list.insertAdjacentHTML('beforeend', '<li>JavaScript</li>');
  $list.insertAdjacentHTML('afterend', '<p>Для фронтенд разработчиков</p>');
</script>

Результат:

  <h2>Веб-технологии</h2>  <!-- beforebegin -->
  <ul id="list"> <!-- целевой элемент -->
    <li>HTML</li> <!-- afterbegin -->
    <li>CSS</li>
    <li>JavaScript</li> <!-- beforeend -->
  </ul>
  <p>Для фронтенд разработчиков</p> <!-- afterend -->

DocumentFragment

DocumentFragment – это облегчённая версия Document. Он используется в качестве обёртки для временного хранения HTML элементов.

После формирования фрагмента его можно использовать в различных методах (например, append, prepend и др.). При этом, когда мы его вставляем, то вставляется только его содержимое.

DocumentFragment в основном используется, когда необходимо вставить множество элементов на страницу, а также для элемента <template>.

Например, переместим все четные <li> в новый <ul>:

<ul id="source-list">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
  <li>Four</li>
</ul>

<ul id="target-list"></ul>

<script>
  const $evenLi = document.querySelectorAll('#source-list li:nth-child(even)');
  // создадим пустой фрагмент
  let $fragment = new DocumentFragment();
  $evenLi.forEach(($li) => {
    // добавим в фрагмент элемент $li
    $fragment.appendChild($li);
  });
  // вставим фрагмент в #target-list
  document.querySelector('#target-list').appendChild($fragment);
</script>

Ещё один пример, в котором добавим в <ul> десять <li>:

<ul id="list"></ul>

<script>
  const $list = document.querySelector('#list');
  // создадим пустой фрагмент
  let $fragment = new DocumentFragment();
  for(let i = 0; i < 10; i++) {
    const $li = document.createElement('li');
    $li.textContent = 'item-' + i;
    // добавим в фрагмент элемент $li
    $fragment.appendChild($li);
  }
  // вставим фрагмент в #target-list
  document.querySelector('#list').append($fragment);
</script>

Использование DocumentFragment в подобных сценариях может значительно ускорить ваш сайт. Т.к. изменение DOM — это очень затратная операция. А с помощью DocumentFragment это можно сделать всего за одну операцию.

DocumentFragment не является частью видимой DOM. Изменения, внесенные во фрагмент, не влияют на документ и производительность страницы.

При использовании современных методов для вставки элементов можно не использовать DocumentFragment, т.к. в отличие от appendChild и insertBefore они позволяют вставлять сразу массив элементов.

Например, перепишем первый пример с использованием append:

<ul id="source-list">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
  <li>Four</li>
</ul>

<ul id="target-list"></ul>

<script>
  const $evenLi = document.querySelectorAll('#source-list li:nth-child(even)');
  // создадим пустой массив
  let $list = [];
  $evenLi.forEach(($li) => {
    // добавим в массив $target элемент $li
    $list.appendChild($li);
  });
  // вставим массив элементов в #target-list
  document.querySelector('#target-list').append(...$list);
</script>

Замена и клонирование узлов

Замену одних узлов другими в JavaScript можно выполнить с помощью методов replaceChild (когда нужна поддержка «старых» браузеров) и replaceWith.

replaceChild

replaceChild предназначен для замены одного дочернего узла parentNode другим:

parentNode.replaceChild(newChild, oldChild);

Где:

  • newChild – элемент, которым необходимо заменить oldChild;
  • parentNode – родительский узел по отношению oldChild.

В качестве результата данный метод возвращает узел, который был заменён новым узлом, т.е. oldChild.

Например, заменим в <ul> второй <li> на новый с текстом «Five».

<ul id="list">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

<script>
  $two = document.querySelector('#list li:nth-child(2)');
  // создадим новый элемент
  $newLi = document.createElement('li');
  $newLi.textContent = 'Five';
  // заменим $two на $newLi $two.parentNode.replaceChild($newLi, $two);
</script>

replaceWith

node.replaceWith позволяет node заменить заданными узлами или строками:

node.replaceWith(...nodes, strings)

Например, заменим в <ul> второй <li> другими элементами:

<ul id="list">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

<script>
  $two = document.querySelector('#list li:nth-child(2)');
  // создадим новые элементы
  $newLi1 = document.createElement('li');
  $newLi1.textContent = 'Five';
  $newLi2 = document.createElement('li');
  $newLi2.textContent = 'Six';
  // заменим $two на $newLi1 и $newLi2
  $two.replaceWith($newLi1, $newLi2);
</script>

cloneNode – клонирование узла

cloneNode предназначен для создания копии узла:

let copy = node.cloneNode(deep);

Где:

  • node – узел, который нужно клонировать;
  • copy – переменная, в которую нужно поместить новый узел, который будет копией node;
  • deep – глубина клонирования (по умолчанию false, т.е. выполняется клонирование только самого элемента node без детей); если установить true, то node будет скопирован со всеми его детьми.

Например, скопируем <ul> и вставим её в конец <body>.

<ul id="list">
  <li>One</li>
  ...
</ul>

<script>
  // выбираем #list
  const $list = document.querySelector('#list');
  // клонируем $list и помещает его в $copy
  const $copy = $list.cloneNode(true);
  // вставляем $copy в конец <body>
  document.body.append($copy);
</script>

Удаление узлов

Удалить узел из DOM можно в JavaScript с помощью методов removeChild (считается устаревшим) и remove.

removeChild

Синтаксис removeChild:

parent.removeChild(node)

Для удаления узла необходимо вызвать метод removeChild у родительского элемента и передать ему в качестве аргумента его сам (node).

Например, удалим второй <li> в <ol>:

<ol id="devices">
  <li>Смартфон</li>
  <li>Планшет</li>
  <li>Ноутбук</li>
</ol>

<script>
const $liSecond = document.querySelector('#devices li:nth-child(2)');
// вызываем у родительского элемента метод removeChild и передаём ему в качестве аргумента узел который нужно удалить
$liSecond.parentNode.removeChild($liSecond);
</script>

В качестве результата метод removeChild возвращает удалённый узел.

Например, удалим элемент, а затем вставим его в другое место:

<div id="message-1">
  <p>...</p>
</div>

<div id="message-2"></div>

<script>
  const $p = document.querySelector('#message-1>p');
  // удалим элемент p
  const result = $p.parentElement.removeChild($p);
  // вставим удалённый элемент p в #message-2
  document.querySelector('#message-2').append(result);
</script>

remove

Ещё один способ удалить узел – это использовать метод remove.

Синтаксис remove:

node.remove()

Например, удалим элемент при нажатии на него:

<button>Кнопка</button>

<script>
document.querySelector('button').onclick = function() {
  // удалим элемент
  this.remove();
}
</script>

Когда мы вставляем элементы, они удаляются со старых мест.

Задачи

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

2. Создать список, текстовое поле и 2 кнопки. Написать код на языке JavaScript, который в зависимости от нажатой кнопки добавляет текст, находящийся в текстовом поле, в начало или в конец списка.

3. Напишите функцию, удаляющую все текстовые узлы у элемента.

4. Имеется 2 списка (<ul>...</ul>), напишите код на языке JavaScript, удаляющий все элементы из 1 и 2 списка.

5. Удалить содержимое элемента, т.е. все его дочерние узлы

6. Заменить текстовый узел "Модель" текстовым узлом "Чертёж", который необходимо создать с помощью метода createTextNode().

<select id="myList"><li>Схема</li><li>Модель</li><li>Эскиз</li></select>

7. Поменяйте местами первый и последний дочерние узлы у списка ul с id="myList".

Комментарии: 5

Evgen
Evgen
Добрый день! Подскажи, пожалуйста, по первой задаче из «Добавление узлов дерева». Как ее решить было бы «правильно», как бы решал ты сам?
Я решил таким образом:
<!-- Имеется два списка в документе. Необходимо переместить элементы из второго списка в первый -->
<ul id="first">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>
<ul id="second">
  <li>Four</li>
  <li>Five</li>
  <li>Six</li>
</ul>

<script>
  let elemUL_FIRST = document.getElementById('first');
  let elemUL_SECOND = document.getElementById('second');
  for (let elem of document.querySelectorAll('#second li')) {
    elemUL_FIRST.insertBefore(elem, elemUL_FIRST.lastChild);
  }
</script>
Александр Мальцев
Александр Мальцев
Привет! Её можно решить различными способами. Просто когда мы вставляем по одному элементу — это не лучший вариант, т.к. изменение DOM довольно дорогая операция. Лучше все сразу.
Набросал 4 различных способа (1 лучше не использовать, сделал для примера):
<!-- Первый список -->
<ul id="first">
  <li>One</li>
  <li>Two</li>
  <li>Three</li>
</ul>

<!-- Второй список -->
<ul id="second">
  <li>Four</li>
  <li>Five</li>
  <li>Six</li>
  <li>Seven</li>
</ul>

<script>
  const $firstList = document.querySelector('#first');
  const $secondList = document.querySelector('#second');

  // 1 способ - с использованием appendChild
  while ($secondList.children.length) {
    $firstList.appendChild($secondList.children[0]);
    // или
    // $firstList.insertBefore($secondList.children[0], null);
  }

  // 2 способ - с использованием appendChild и DocumentFragment
  let $fragment = new DocumentFragment;
  while ($secondList.children.length) {
    $fragment.appendChild($secondList.children[0]);
  }
  $firstList.appendChild($fragment);

  // 3 способ - с использованием append, DocumentFragment и forEach
  let $fragment = new DocumentFragment;
  Array.from($secondList.children).forEach(($element) => {
    $fragment.append($element);
  });
  $firstList.append($fragment);

  // 4 способ - с использованием insertAdjacentHTML
  $firstList.insertAdjacentHTML('beforeend', $secondList.innerHTML);
  $secondList.textContent = '';	
</script>
Ссылка на пример: modifying-document-task-01
Evgen
Evgen
MDN пишет про второй параметр «Node.insertBefore()»:

В Mozilla Firefox, если referenceElement не задан или равен null, newElement вставляется в конец списка дочерних элементов. В IE, referenceElement равный undefined, сгенерируется исключение "Invalid argument", в то время как Chrome сгенерирует исключение  "Uncaught TypeError", ожидая 2 аргумента."
Возможно, нужно немного исправить выше. Столкнулся с ошибкой в Хроме, не указав второй параметр.
Александр Мальцев
Александр Мальцев
Спасибо, поправил этот момент.
Архаил
Архаил
Я пытаюсь реализовать механизм, где к примеру по клику на кнопку появляется список в имеющимся родительском элементе div по событию onchange передаю выбранные параметры в функцию js, далее отправляю данные на сервер, получаю следующий список данных, возвращаю в success, после использую шаблоны template(js)
чтобы прокрутить в цикле получить данные и подставить значение в тег списка в этом же теге снова передаю данные с помощью onchange той же функции js, где она должна так же отправить данные на сервер, получить ответ и создать следующий DOM элемент div с другим id вставив тег select>option и вывести следом за предыдущим в родительском элементе…
То есть при каждом возврате данных с сервера мне надо чтобы создавался DOM элемент, в него добавлялся тег или к примеру(в зависимости от типа поля).



itchief.ru/assets/uploadify/a/9/b/a9ba2d009d894505e538eb5f9440ac5b.png

При первом вызове этой функции все отрабатывает правильно, создает элемент, отображается в DOM структуре, при повторном попадании создает также элемент div присваивает ему id, но когда использую appendChild() ругается: Cannot read property 'appendChild' of null. Я как понимаю скрипт отрабатывается раньше загрузки разметки… Какими путями можно решить данную задачу?