JavaScript — изменение документа
Как изменять DOM с помощью JavaScript: создавать, вставлять, заменять и удалять узлы, использовать insertAdjacentHTML, textContent и innerHTML.
JavaScript позволяет изменять страницу после её загрузки: добавлять новые элементы, перемещать их, обновлять текст, менять атрибуты и удалять то, что больше не нужно пользователю. Это основа любого динамического интерфейса — от отображения сообщения о валидации до рендеринга целого списка результатов поиска.
На этой странице рассматривается практический набор инструментов для изменения DOM: создание и вставка узлов, разница между textContent и innerHTML, быстрая вставка HTML с помощью insertAdjacentHTML, удаление и замена узлов, а также использование DocumentFragment для ускорения обновлений. Чтобы найти элементы, которые нужно изменить, см. Поиск: getElement* и querySelector*. Для понимания общей картины того, как страница структурирована в виде дерева узлов, см. Понимание узлов DOM.
Создание новых узлов
Прежде чем вставить что-либо на страницу, это нужно создать. Есть два основных строительных блока: узлы-элементы (<div>, <li>, …) и текстовые узлы (обычный текст).
Создание элементов
Новые элементы создаются с помощью метода document.createElement(tag). Элемент существует только в памяти до тех пор, пока он не вставлен в документ — до этого момента пользователь ничего не видит. Вот как создать div и добавить его на страницу:
<body></body>
<script>
let div = document.createElement('div');
div.innerHTML = "Hello, world!";
document.body.appendChild(div);
</script>Элемент появится только после того, как appendChild (или другой метод вставки) прикрепит его к узлу, уже находящемуся в документе.
Создание текстовых узлов
Для добавления обычного текста используется document.createTextNode(text). В отличие от innerHTML, текстовый узел обрабатывает содержимое буквально — <b> остаётся символами <b>, а не превращается в тег жирного шрифта. Это делает текстовые узлы безопасным выбором, когда текст приходит от пользователя или из любого ненадёжного источника:
<body>
<div id="container"></div>
</body>
<script>
const div = document.getElementById("container");
let textNode = document.createTextNode('Here is some text');
div.appendChild(textNode);
</script>Вставка узлов
Когда узел создан, его нужно куда-то поместить. Классический метод — parent.appendChild(node), который добавляет узел последним дочерним элементом. Современные браузеры также предоставляют более гибкое семейство методов, принимающих как узлы, так и строки:
| Метод | Куда вставляет |
|---|---|
node.append(...) | В конец, внутрь node |
node.prepend(...) | В начало, внутрь node |
node.before(...) | Непосредственно перед node |
node.after(...) | Непосредственно после node |
node.replaceWith(...) | Полностью заменяет node |
Здесь append и prepend добавляют элементы списка на противоположные концы <ul>:
<body>
<ul id="list">
<li>middle</li>
</ul>
</body>
<script>
const list = document.getElementById("list");
const first = document.createElement("li");
first.textContent = "first";
list.prepend(first);
const last = document.createElement("li");
last.textContent = "last";
list.append(last);
</script>insertAdjacentHTML
Когда нужно вставить строку HTML относительно элемента — не затрагивая существующее содержимое — для этого подходит element.insertAdjacentHTML(position, html). Аргумент position — одно из четырёх ключевых слов:
"beforebegin"— перед самим элементом"afterbegin"— внутри, перед первым дочерним элементом"beforeend"— внутри, после последнего дочерního элемента"afterend"— после самого элемента
<body>
<div id="box">existing content</div>
</body>
<script>
const box = document.getElementById("box");
box.insertAdjacentHTML("beforeend", "<p>added inside, at the end</p>");
box.insertAdjacentHTML("afterend", "<p>added after the box</p>");
</script>Поскольку insertAdjacentHTML разбирает HTML-строку, никогда не передавайте в него ненадёжные данные — применяется тот же риск XSS, что и с innerHTML. Существуют методы-аналоги insertAdjacentText и insertAdjacentElement для вставки обычного текста или существующего узла в те же позиции.
textContent и innerHTML
Оба свойства задают содержимое элемента, но ведут себя совершенно по-разному:
innerHTMLразбирает строку как HTML.el.innerHTML = "<b>Hi</b>"создаёт жирный узел. Присваивание заменяет все существующие дочерние элементы.textContentобрабатывает строку как обычный текст.el.textContent = "<b>Hi</b>"показывает буквальные символы<b>Hi</b>.
Предпочитайте textContent при вставке текста — это быстрее и защищает от внедрения HTML/скриптов. Используйте innerHTML только тогда, когда действительно нужна разметка и источник заслуживает доверия.
<body>
<div id="asHtml"></div>
<div id="asText"></div>
</body>
<script>
const input = "<b>3 < 5</b>";
document.getElementById("asHtml").innerHTML = input; // renders bold text
document.getElementById("asText").textContent = input; // shows the literal markup
</script>Изменение элементов
Изменение атрибутов
Чтобы изменить атрибут элемента, можно использовать element.setAttribute(name, value). Этот метод позволяет задать значение атрибута указанного элемента.
<body>
<div id="firstID"></div>
<div>new id is: <span id="result"></span></div>
</body>
<script>
const div = document.getElementById("firstID");
const result = document.getElementById("result");
div.setAttribute("id", "newDiv");
result.innerHTML = div.id;
</script>Для более детального изучения различия между HTML-атрибутами и свойствами DOM (и когда использовать setAttribute, а когда прямой доступ к свойству, например el.id = "..."), см. Атрибуты и свойства.
Расширенные манипуляции
Клонирование элементов
Копию элемента можно создать с помощью метода element.cloneNode(deep). При значении deep равном true клонируется элемент вместе со всеми его потомками.
<body>
<div id="mydiv">one div here, or two divs if cloned! <span>And here is a span inside the div!</span></div>
</body>
<script>
const div = document.getElementById("mydiv");
const clone = div.cloneNode(true);
document.body.appendChild(clone);
</script>Замена элементов
Чтобы заменить один узел другим, вызовите element.replaceWith(newNode). Старый узел удаляется из документа, а новый занимает его место:
<body>
<p id="old">I will be replaced.</p>
</body>
<script>
const old = document.getElementById("old");
const fresh = document.createElement("p");
fresh.textContent = "I am the replacement.";
old.replaceWith(fresh);
</script>Удаление элементов
Чтобы удалить элемент из DOM, используйте element.remove().
<body>
<div>It's the only thing you see, as the next div is removed!</div>
<div id="mydiv">you don't see me if I'm removed!</div>
</body>
<script>
const div = document.getElementById("mydiv");
div.remove();
</script>Пакетные обновления с DocumentFragment
Каждый раз, когда вы вставляете узел в живой документ, браузер может пересчитывать макет. Вставка множества узлов в цикле по одному за раз может поэтому оказаться медленной. DocumentFragment — это лёгкий внеэкранный контейнер: вы собираете в нём все узлы, а затем вставляете фрагмент один раз. В DOM попадают только его дочерние элементы — сам фрагмент исчезает.
<body>
<ul id="list"></ul>
</body>
<script>
const list = document.getElementById("list");
const fragment = document.createDocumentFragment();
for (let i = 1; i <= 5; i++) {
const li = document.createElement("li");
li.textContent = "Item " + i;
fragment.appendChild(li);
}
// One single insertion into the live document
list.appendChild(fragment);
</script>Другие способы поддерживать эффективность обновлений DOM см. в Оптимизация производительности DOM. Если ваша разметка используется повторно, элемент <template> — ещё один быстрый способ клонировать готовую структуру.
Практический пример: создание списка задач
Применим эти концепции для создания простого списка задач на JavaScript:
<body></body>
<script>
// Creating the list container
let list = document.createElement('ul');
document.body.appendChild(list);
// Adding items to the list
function addItem(text) {
let item = document.createElement('li');
item.textContent = text;
list.appendChild(item);
}
addItem('Learn JavaScript');
addItem('Build a to-do list');
</script>Заключение
Изменение документа — это основа любого динамического интерфейса: создавайте узлы с помощью createElement/createTextNode, размещайте их с помощью append/prepend/before/after или insertAdjacentHTML, выбирайте textContent для безопасного текста и innerHTML только для доверенной разметки, а DocumentFragment используйте для ускорения массовых обновлений. Применяйте replaceWith и remove для замены или удаления узлов.
Для дальнейшего изучения см. Манипуляции с DOM для общего обзора, Обход DOM для перемещения между связанными узлами и Свойства узла: тип, тег и содержимое, чтобы понять, что содержит каждый узел.