DOM, часть 2 из 3. Изменение документа

08.05.2022

Теги: DOMFrontendHTMLJavaScriptWeb-разработкаДокументТеория

В соответствии с объектной моделью документа («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:

&lt;p&gt;Какой-то текст&lt;/p&gt;
<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">&lt;em&gt;Новый контент элемента&lt;/em&gt;</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 • Frontend • HTML • JavaScript • Web-разработка • Документ • Теория

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.