DOM, часть 2 из 3. Изменение документа
08.05.2022
Теги: DOM • Frontend • HTML • JavaScript • Web-разработка • Документ • Теория
В соответствии с объектной моделью документа («Document Object Model», коротко DOM), каждый HTML-тег является объектом. Вложенные теги являются «детьми» родительского элемента. Текст, который находится внутри тега, также является объектом. Модификации DOM — это ключ к созданию «живых» страниц.
Создание DOM-узла
DOM-узел HTML-документа — это элемент (HTML-тег) или текст (текстовой узел). Соответственно, есть два метода объекта document
для создания узла:
let divElem = document.createElement('div');
let textNode = document.createTextNode('текстовой узел');
Методы вставки
Чтобы созданный элемент появился на странице, нам нужно вставить его где-нибудь в document
— например, в document.body
:
let divElem = document.createElement('div'); divElem.textContent = 'содержимое элемента'; document.body.append(divElem);
Вот методы для различных вариантов вставки:
node.append(...nodes or strings)
— добавляет узлы или строки в конецnode
node.prepend(...nodes or strings)
— вставляет узлы или строки в началоnode
node.before(...nodes or strings)
— вставляет узлы или строки доnode
node.after(...nodes or strings)
— вставляет узлы или строки послеnode
node.replaceWith(...nodes or strings)
— заменяетnode
заданными узлами или строками
Вот пример использования этих методов, чтобы добавить новые элементы в список и текст до/после него:
<ol id="list"> <li>раз</li> <li>два</li> <li>три</li> </ol> <script> list.before('before'); // вставить строку «before» перед <ol> list.after('after'); // вставить строку «after» после <ol> let liFirst = document.createElement('li'); liFirst.innerText = 'prepend'; list.prepend(liFirst); // вставить элемент в начало <ol> let liLast = document.createElement('li'); liLast.innerText = 'append'; list.append(liLast); // вставить элемент в конец <ol> </script>
before <ol id="list"> <li>prepend</li> <li>раз</li> <li>два</li> <li>три</li> <li>append</li> </ol> after
Эти методы могут вставлять несколько узлов и текстовых фрагментов за один вызов:
<div id="div"></div> <script> button = document.createElement('button'); button.textContent = 'Кнопка'; div.before('<p>Какой-то текст</p>', button); </script>
Весь текст вставляется как текст — безопасным способом, как делает это textContent
:
<p>Какой-то текст</p> <button>Кнопка</button> <div id="div"></div>
Свойство textContent
Свойство textContent
предназначено для работы с текстовым контентом элемента. Оно позволяет его как получить (включая текст всех потомков), так и установить.
<div id="div">Какой-то контент элемента</div> <script> const divText = div.textContent; alert(divText); // Какой-то контент элемента div.textContent = 'Новый контент элемента'; const newText = div.textContent; alert(newText); // Новый контент элемента </script>
Для элемента, который содержит множество других узлов, textContent
вернёт конкатенацию (сложение) текстов всех его текстовых узлов. При установке элементу текстового содержимого, textContent
удалит всего его узлы (при их наличии), и добавит в него один текстовый узел, содержащий указанный текст.
Если присвоить textContent
строку, содержащую HTML-теги, то она будет вставлена безопасным способом:
<div id="div">Какой-то контент элемента</div> <script> div.textContent = '<em>Новый контент элемента</em>'; </script>
<div id="div"><em>Новый контент элемента</em></div>
innerText и outerText
Свойство innerText
, как и свойство textContent
, предназначено для работы с текстовым контентом элемента. Но в отличие от textContent
, как бы копирует текст, отображаемый этим элементом в браузере. Когда дочерний элемент скрыт, innerText
не включает его контент в возвращаемое значение. Кроме этого, innerText
не добавляет в возвращаемый результат содержимое тегов <style>
и <script>
.
При установке элементу текстового контента, innerText
удаляет все имеющиеся в нём узлы и создаёт новый текстовый узел с указанным текстом.
Свойство outerText
возвращает текстовое содержимое элемента аналогично свойству innerText
. Его отличие от innerText
только в том, что outerText
при установки элементу текстового контента удаляет не только всё его содержимое, но и сам этот элемент и помещает на этом месте новый текстовый узел с заданным текстом.
innerHTML и outerHTML
Свойство innerHTML
предназначено для установки или получения HTML-разметки элемента. Задание HTML-содержимого элементу с помощью innerHTML
всегда сопровождается удалением его контента и установкой ему новой HTML-разметки на основе указанной строки, которая была разобрана внутренним парсером браузера как HTML.
<ol id="list"> <li>раз</li> <li>два</li> <li>три</li> </ol> <script> list.innerHTML = '<li>четыре</li><li>пять</li><li>шесть</li>'; </script>
<ol id="list"> <li>четыре</li> <li>пять</li> <li>шесть</li> </ol>
Свойство outerHTML
устанавливает или возвращает HTML-контент, представляющий сам элемент и его дочерние элементы.
<ol id="list"> <li>раз</li> <li>два</li> <li>три</li> </ol> <script> list.outerHTML = '<ol id="list"><li>четыре</li><li>пять</li><li>шесть</li></ol>'; </script>
<ol id="list"> <li>четыре</li> <li>пять</li> <li>шесть</li> </ol>
insertAdjacentHTML/Text/Element
Метод insertAdjacentHTML(where, html)
позволяет вставлять произвольную HTML-разметку в любое место документа, в том числе и между узлами. Первый параметр — это специальное слово, указывающее, куда по отношению к элементу производить вставку. Второй параметр — это HTML-строка, которая будет вставлена как HTML-разметка.
<ol id="list"> <li>раз</li> <li>два</li> <li>три</li> </ol> <script> list.insertAdjacentHTML('beforebegin', '<p>before begin</p>'); // вставить <p>before begin</p> перед <ol> list.insertAdjacentHTML('afterend', '<p>after end</p>'); // вставить <p>after end</p> после <ol> list.insertAdjacentHTML('afterbegin', '<li>after begin</li>'); // вставить <li>after begin</li> в начало <ol> list.insertAdjacentHTML('beforeend', '<li>before end</li>'); // вставить <li>before end</li> в конец <ol> </script>
<p>before begin</p> <ol id="list"> <li>after begin</li> <li>раз</li> <li>два</li> <li>три</li> <li>before end</li> </ol> <p>after end</p>
У метода insertAdjacentHTML(where, html)
есть два брата:
insertAdjacentText(where, text)
— такой же синтаксис, но строкаtext
вставляется как текстinsertAdjacentElement(where, elem)
— такой же синтаксис, но вставляет элементelem
Удаление узлов
Для удаления узла из документа предназначен метод remove
:
<div id="div">Этот узел будет удален через 3 секунды</div> <script> setTimeout(() => div.remove(), 3000); </script>
Если нужно переместить элемент в другое место — нет необходимости удалять его со старого. Все методы вставки автоматически удаляют узлы со старых мест.
<div id="one">Раз</div> <div id="two">Два</div> <script> setTimeout(() => { // нет необходимости вызывать метод remove two.after(one); // вставить #one после #two }, 3000); </script>
Клонирование узлов
Метод cloneNode
позволяет клонировать элемент и получить его точную копию. Эту копию затем можно добавить на страницу с помощью методов вставки. В параметре метод получает true
либо false
. Если передан true
, то элемент клонируется полностью, вместе со всем атрибутами и дочерними элементами, а если false
— только сам элемент, без дочерних элементов.
<div id="one">Какой-то элемент страницы</div> <script> setTimeout(() => { const cloneOne = one.cloneNode(true); cloneOne.id = 'two'; // чтобы id были разными cloneOne.textContent = 'Клонированный элемент'; one.after(cloneOne); // вставить клона после #one }, 3000); </script>
DocumentFragment
DocumentFragment
является специальным DOM-узлом, который служит обёрткой для передачи списков узлов. Мы можем добавить к нему другие узлы, но когда мы вставляем его куда-то, он «исчезает», вместо него вставляется его содержимое.
<ul id="list"></ul> <script> function getListContent() { const items = ['раз', 'два', 'три'] const content = new DocumentFragment(); items.forEach(item => { const li = document.createElement('li'); li.append(item); content.append(li); }) return content; } list.append(getListContent()); </script>
<ul id="list"> <li>раз</li> <li>два</li> <li>три</li> </ul>
DocumentFragment
редко используется. Зачем добавлять элементы в специальный вид узла, если вместо этого мы можем вернуть массив узлов?
<ul id="list"></ul> <script> function getListContent() { const items = ['раз', 'два', 'три'] const result = items.map(item => { const li = document.createElement('li'); li.append(item); return li; }) return result; } list.append(...getListContent()); </script>
<ul id="list"> <li>раз</li> <li>два</li> <li>три</li> </ul>
Устаревшие методы
Есть несколько других, более старых, методов вставки и удаления, которые существуют по историческим причинам. Сейчас уже нет причин их использовать, так как современные методы append
, prepend
, before
, after
, remove
, replaceWith
более гибкие и удобные.
elem.appendChild(node)
— добавляетnode
в конец дочерних элементовelem
elem.insertBefore(node, nextSibling)
— вставляетnode
передnextSibling
вelem
elem.replaceChild(node, oldChild)
— заменяетoldChild
наnode
среди дочерних элементовelem
elem.removeChild(node)
— удаляет дочернийnode
изelem
- DOM, часть 4 из 4. Атрибуты и свойства
- DOM, часть 3 из 3. Поиск элементов
- DOM, часть 1 из 3. Навигация по элементам
- Браузерные события, часть 5 из 5. Генерация пользовательских событий
- Браузерные события, часть 4 из 5. Действие браузера по умолчанию
- Браузерные события, часть 3 из 5. Делегирование
- Браузерные события, часть 2 из 5. Погружение и всплытие
Поиск: DOM • Frontend • HTML • JavaScript • Web-разработка • Документ • Теория