Скидки до 50% и курс по ИИ в подарок 2 дня 12 :27 :27 Выбрать курс
Код Справочник по фронтенду
#статьи

Объектная модель документа (DOM): что это и как она устроена

Разбираемся, как JavaScript взаимодействует с HTML.

Иллюстрация Polina Vari для Skillbox Media

DOM — это механизм, благодаря которому браузер понимает и отображает HTML‑страницу.

Когда браузер считывает код, он превращает его в структуру из взаимосвязанных объектов: заголовков, абзацев, изображений и других элементов. Эти объекты можно изменять с помощью JavaScript: добавлять новые, удалять лишние, менять текст или оформление.

DOM нужен для того, чтобы JavaScript мог взаимодействовать со страницей уже после её загрузки. Без него браузер просто показал бы набор тегов и текста, а страница оставалась бы статичной. Благодаря DOM код может изменить текст кнопки, скрыть блок, добавить абзац или отреагировать на нажатие.

Именно DOM делает веб-страницы интерактивными — они могут меняться прямо в браузере, без повторной загрузки.

Содержание


Как устроен DOM

Дерево — это способ хранения данных, при котором элементы выстраиваются в иерархию: один является «родителем», а другие — его «потомками».

У каждого узла (элемента) может быть несколько подузлов, но только один родитель. На самом верху находится корень — главный элемент, с которого всё начинается. У корня нет родителя, а узлы без потомков называются листьями.

Классический пример такой структуры — папки на компьютере. Корнем может быть диск C, внутри него — папка «Пользователи», а в ней — отдельные каталоги для каждого пользователя. Чтобы добраться до файла, нужно пройти по веткам дерева от корня к нужному листу.

DOM (Document Object Model, объектная модель документа) — это то же самое дерево, только вместо папок и файлов в нём находятся элементы веб-страницы.

Когда вы открываете сайт, браузер получает HTML-код и превращает его в иерархию объектов. Каждый тег — это отдельный узел этого дерева: тег <html> — корень, внутри него теги <head> и <body>, внутри <body> — параграфы, ссылки, изображения и так далее.

<!DOCTYPE html>
<html>
<head>
    <title>Пример</title>
</head>
<body>
    <h1>Заголовок</h1>
    <p>Это параграф.</p>
</body>
</html>

С помощью DOM можно добавлять или удалять элементы, менять текст и стили, реагировать на действия пользователя. Когда вы нажимаете кнопку и на странице появляется всплывающее окно — это работа с DOM: скрипт изменяет дерево, и браузер сразу обновляет то, что вы видите.

Как HTML-документ превращается в DOM-дерево

Браузер читает HTML сверху вниз. Он видит первый тег <html> и создаёт корневой элемент дерева — то, от чего потом отрастают ветви. Затем встречает <head> и <body> — добавляет их как дочерние узлы. Так постепенно из каждого тега появляется объект: у <p> — узел параграфа, у <img> — узел изображения, у <a> — ссылка. Всё, что находится между тегами, превращается в текстовые узлы.

Когда браузер доходит до конца документа, дерево готово. Это и есть DOM — внутренняя модель страницы. Теперь JavaScript может преображать её как угодно: добавлять элементы, менять стили, удалять текст. А браузер мгновенно обновляет интерфейс, потому что следит за деревом и перерисовывает то, что изменилось.

Вот короткий пример HTML и его DOM-дерева:

<!DOCTYPE html>
<html>
  <body>
    <h1>Привет, мир!</h1>
    <p>Это абзац текста.</p>
  </body>
</html>

DOM-дерево для этого кода выглядит так:

Document
├─ DocumentType: html
└─ Element: html
   └─ Element: body
      ├─ Element: h1
      │  └─ Text: "Привет, мир!"
      └─ Element: p
         └─ Text: "Это абзац текста."

Здесь:

  • Document — корень документа.
  • DocumentType — объявление <!DOCTYPE html>.
  • Element — HTML-элементы: <html>, <body>, <h1>, <p>.
  • Text — текстовое содержимое тегов.

Из каких типов узлов состоит DOM

DOM-дерево устроено так, что каждая часть HTML превращается в узел (node) определённого типа. Таких типов немного, но вместе они описывают всё, что есть в документе.

Узел документа (Document) — это корневой объект всего DOM-дерева, отправная точка, с которой начинается работа со страницей.

Когда браузер загружает HTML, он создаёт объект document, в котором хранится всё содержимое страницы: теги, текст, скрипты, стили и даже комментарии. Можно сказать, что document — это представление всей веб-страницы в памяти браузера.

Например, если в HTML написано:

<!DOCTYPE html>
<html>
  <head>
    <title>Пример</title>
  </head>
  <body>
    <p>Привет, мир!</p>
  </body>
</html>

То в JavaScript вы можете обратиться к разным частям этого документа так:

document.title;       // "Пример"
document.body;        // <body>...</body>
document.documentElement; // <html>...</html>

Объект document — это и узел, и глобальная точка доступа к DOM. Через него создают, находят и изменяют элементы:

const p = document.createElement('p'); // Создаём новый элемент
p.textContent = 'Новый текст';
document.body.appendChild(p);          // Добавляем его на страницу

Узел элемента (element node) — любой HTML-тег на странице.Элементы могут содержать другие элементы и текст.

Каждый раз, когда браузер встречает тег вроде <div>, <p>, <img> или <button>, он создаёт узел элемента. Этот узел хранит всю информацию о теге: его имя, атрибуты, дочерние элементы и текст внутри.

Например, возьмём фрагмент HTML:

<div id="container">
  <p class="text">Привет, мир!</p>
</div>
  • Элемент div — это корневой узел внутри этого фрагмента.
  • У него есть дочерний элемент p — тоже узел элемента
  • Внутри <p> есть текстовый узел, который хранит строку Привет, мир!.

Доступ к узлу элемента можно получить через JavaScript:

// Находим элемент <p> — это узел элемента
const paragraph = document.querySelector('.text');
// Получаем информацию об узле
console.log(paragraph.nodeType); // 1 — тип узла «элемент»
console.log(paragraph.nodeName); // P — имя тега
console.log(paragraph.tagName);  // Тоже P

Тип 1 указывает, что это именно element node, а не, например, текст (3) или комментарий (8).

Через такие узлы JavaScript управляет страницей: меняет текст (textContent), классы (classList), стили (style) и структуру (append, remove, cloneNode).

Текстовый узел (Text) — это часть DOM-дерева, в которой хранится текст. Когда браузер разбирает HTML, он превращает каждый кусочек текста между тегами в отдельный узел. У такого узла нет атрибутов, классов или дочерних элементов — только текстовое содержимое.

Например, возьмём простой HTML-код:

<p>Привет, мир!</p>

В DOM это превращается в структуру:

  • узел элемента <p>
    └── текстовый узел "Привет, мир!"

Проверим в JavaScript:

const p = document.querySelector('p');
console.log(p.firstChild.nodeType); // 3 — это текстовый узел
console.log(p.firstChild.nodeValue); // "Привет, мир!"

Здесь:

nodeType === 3 означает, что перед нами текстовый узел.

Можно создать такой узел вручную:

const text = document.createTextNode('Новый текст');
document.body.appendChild(text);

Или просто задать текст элементу:

const p = document.createElement('p');
p.textContent = 'Новый абзац';
document.body.appendChild(p);

Оба способа создают текстовый узел внутри тега.

Атрибут (Attribute) это часть DOM, которая описывает дополнительные свойства HTML-элемента: класс, идентификатор, ссылку, источник изображения и так далее. В DOM атрибуты тоже представлены как узлы, хотя напрямую в дереве они обычно не отображаются — они принадлежат элементу.

Когда браузер разбирает HTML, он создаёт для каждого атрибута отдельный узел, связанный с конкретным элементом. Например:

<img src="cat.jpg" alt="Кот">

В DOM-дереве у тега <img> есть два узла атрибута:

  • src со значением "cat.jpg";
  • alt со значением "Кот".

Каждый такой узел содержит пару «имя — значение». Если посмотреть через JavaScript:

const img = document.querySelector('img');
console.log(img.getAttribute('src')); // "cat.jpg"
console.log(img.attributes[0].name);  // "src"
console.log(img.attributes[0].value); // "cat.jpg"

Раньше атрибуты считались отдельным типом узлов (nodeType === 2), но современные браузеры хранят их как свойства элемента. С ними работают через методы:

img.setAttribute('alt', 'Милый кот');  // Изменить значение
img.getAttribute('alt');               // Получить значение
img.hasAttribute('alt');               // Проверить, есть ли атрибут
img.removeAttribute('alt');            // Удалить атрибут

Комментарий (Comment). Даже комментарии из HTML (<!-- комментарий -->) становятся отдельными узлами, хотя на странице их не видно. Это нужно, чтобы JavaScript мог при желании их читать, изменять или удалять.

Например:

<div>Контент</div>
<!-- Это комментарий -->

В DOM это выглядит так:

  • элемент <div>;
  • узел-комментарий с текстом "Это комментарий".

Проверим это в коде:

const comment = document.body.childNodes[1]; // Второй узел — комментарий
console.log(comment.nodeType); // 8 — это тип узла "Comment"
console.log(comment.nodeValue); // "Это комментарий"

Можно создать комментарий вручную:

const newComment = document.createComment('Создан через JS');
document.body.appendChild(newComment);

Как получить доступ к элементам

У JavaScript несколько методов доступа к элементам DOM:

getElementById(). Ищет элемент по уникальному идентификатору. Например, если в коде есть <div id="header">, то в JavaScript его можно получить так:

const header = document.getElementById('header');

После этого header становится объектом, с которым можно работать: менять его содержимое, стили, атрибуты.

getElementsByClassName(). Используют, если есть несколько элементов с одинаковым классом. Метод возвращает коллекцию — не один элемент, а список всех подходящих. Например:

const buttons = document.getElementsByClassName('btn');

Теперь buttons содержит все элементы с классом btn (кнопки).

querySelector() и querySelectorAll(). Более современные и гибкие способы, которые работают как CSS-селекторы. Можно искать элементы по тегу, классу, id или по сложному условию:

const link = document.querySelector('nav a.active'); // Первая активная ссылка в меню
const allLinks = document.querySelectorAll('nav a'); // Все ссылки в навигации

querySelector возвращает первый найденный элемент, querySelectorAll — список всех.

Есть и другие методы: getElementsByTagName() — ищет все элементы с определённым тегом (<p>, <div>), а document.body и document.documentElement позволяют напрямую обратиться к основным частям страницы — телу документа и корневому тегу <html>.

Свойства DOM-элементов

DOM-элементы имеют множество свойств, которые позволяют управлять их содержимым и стилем:

innerHTML — это свойство, которое возвращает или задаёт вложенный HTML-код элемента как строку. Проще говоря, когда вы читаете elem.innerHTML, вы получаете текст с разметкой, внутри тега, а когда присваиваете elem.innerHTML = '…' — браузер парсит строку как HTML и заменяет всё содержимое элемента на новое.

const content = document.querySelector('div').innerHTML;

Если присвоить новое значение, браузер перерисует содержимое элемента:

div.innerHTML = '<p>Новый текст</p>';

Самая серьёзная опасность — XSS: если вставлять через innerHTML данные, полученные от пользователя или из ненадёжного источника, в строке может оказаться вредоносный код.

textContent: получает или устанавливает текстовое содержимое элемента без HTML-тегов.

const text = document.querySelector('p').textContent;

classList: позволяет управлять классами элемента (добавлять, удалять, проверять наличие).

const element = document.querySelector('.my-element');
element.classList.add('active');

style: Позволяет изменять стили элемента через JavaScript.

element.style.color = 'red';

Кроме этих свойств, у элементов есть десятки других: id, value, href, src, dataset и так далее — в зависимости от типа тега. Можно не только найти элемент, но и управлять им как полноценным объектом программы.

Как изменить содержимое и структуру страницы

JavaScript может менять DOM-дерево: добавлять новые ветви, удалять старые или перестраивать узлы. Для этого есть набор методов:

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

const newDiv = document.createElement('div');
document.body.appendChild(newDiv);

prepend(). Добавляет дочерний элемент в начало родительского элемента.

const p2 = document.createElement('p');
p2.textContent = 'Привет в начале!';
document.body.prepend(p2);

insertBefore(). Вставляет элемент перед указанным элементом.

const p3 = document.createElement('p');
p3.textContent = 'Вставлено перед первым абзацем';
document.body.insertBefore(p3, p);

removeChild (node). Удаляет указанный дочерний узел.

const divToRemove = document.querySelector('div');
document.body.removeChild(divToRemove);

replaceChild (newNode, oldNode). Заменяет один дочерний узел другим.

const newParagraph = document.createElement('p');
const oldParagraph = document.querySelector('p');
document.body.replaceChild(newParagraph, oldParagraph);

Как создавать, клонировать и удалять DOM-элементы с помощью JS

Чтобы создать элемент, используют метод:

document.createElement('тег');

Например:

const newDiv = document.createElement('div');
newDiv.textContent = 'Привет!';
document.body.appendChild(newDiv);

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

element.cloneNode(true);

Если передать true, то браузер клонирует узел вместе со всем содержимым: текстом, потомками и атрибутами. Без аргумента (false) копируется только сам элемент, без вложенных тегов. Например:

const cardCopy = card.cloneNode(true);
document.body.appendChild(cardCopy);

Удаление проводится просто — с помощью метода remove():

newDiv.remove();

Он сразу исключает узел из DOM-дерева.

Благодаря этим методам JavaScript может собирать страницу на ходу: добавлять карточки товаров, клонировать шаблоны, убирать лишние блоки. Всё это происходит мгновенно, без перезагрузки, потому что браузер просто меняет части DOM-дерева прямо в памяти.

Что такое атрибуты и как с ними работать

Атрибуты — это дополнительные данные внутри HTML-тегов, которые задают поведение или внешний вид элемента. Например, в ссылке <a href="https://skillbox.ru"> атрибут href указывает адрес, куда ведёт ссылка. Когда страница превращается в DOM, каждый атрибут становится частью объекта элемента. JavaScript может их читать, изменять или удалять прямо во время работы страницы. Для этого существует три базовых метода.

getAttribute() возвращает значение атрибута.

const link = document.querySelector('a');
console.log(link.getAttribute('href'));

Этот код выведет адрес, указанный в теге.

setAttribute() создаёт новый атрибут или меняет существующий:

link.setAttribute('href', 'https://skillbox.ru/media/');

Теперь ссылка ведёт на другой сайт. Если такого атрибута не было, он появится автоматически.

hasAttribute() проверяет, есть ли у элемента нужный атрибут.

if (link.hasAttribute('target')) {
  console.log('Ссылка открывается в новой вкладке');
}

Есть и противоположный метод — removeAttribute(), который удаляет атрибут совсем:

link.removeAttribute('title');

Чем DOM отличается от визуального представления

DOM — это структура данных, а не картинка. Но само по себе DOM-дерево ничего не рисует.

Чтобы превратить эту структуру в видимую страницу, браузер проходит ещё несколько этапов. После создания DOM он разбирает CSS и строит CSSOM — дерево стилей. Затем объединяет оба дерева и формирует render tree — модель, которая описывает, какие элементы должны попасть на экран и как именно они должны выглядеть.

Дальше начинается layout — расчёт размеров, координат и расположения каждого блока. Браузер определяет, сколько пикселей займёт текст, где будут границы, как элементы будут размещены друг относительно друга.

И наконец — рендеринг (painting). На этом этапе браузер действительно рисует на экране всё: текст, цвета, изображения, тени.

DOM участвует в этом процессе как основа, но не совпадает с тем, что вы видите. Например, если элемент скрыт через display: none, он остаётся в DOM, но в визуальном представлении его просто нет. Или наоборот: при CSS-анимации DOM не меняется, хотя на экране происходят движения.

Полезные советы

Сокращайте количество обращений к DOM

Каждый поиск элемента заставляет браузер обходить всё дерево. Если элемент нужен не один раз, сохраните его в переменную.

// Нашли один раз и используем
const button = document.querySelector('.button');
button.textContent = 'OK';
button.classList.add('active');

Объединяйте изменения в памяти

Каждое добавление элемента вызывает перерисовку страницы. Если вставляете много элементов, соберите их сначала в памяти с помощью DocumentFragment, а потом вставьте за один раз.

const list = document.querySelector('ul');
const fragment = document.createDocumentFragment();

for (let i = 1; i <= 5; i++) {
  const li = document.createElement('li');
  li.textContent = `Элемент ${i}`;
  fragment.appendChild(li);
}

list.appendChild(fragment); // Браузер перерисует только один раз

Используйте классы, а не inline-стили

Вместо того чтобы менять цвета, размеры и шрифты прямо через style, лучше добавлять или убирать классы. Так код будет чище, а за оформление станет отвечать CSS.

JS:

element.classList.add('active');

CSS:

.active {
  background-color: red;
  color: white;
}

Делегируйте события

Если элементов много или они создаются динамически, не навешивайте обработчик на каждый. Вместо этого добавьте один обработчик на общий контейнер.

// Вместо множества обработчиков на каждую кнопку:
document.querySelectorAll('.btn').forEach(btn => {
  btn.addEventListener('click', () => console.log('Нажато'));
});

// Один обработчик на родителе:
document.querySelector('.buttons').addEventListener('click', e => {
  if (e.target.matches('.btn')) {
    console.log('Нажато:', e.target.textContent);
  }
});

Пишите читаемый код

Лучше потратить лишние 10 строк, но сделать код понятнее. Длинные функции разбивайте на небольшие, давайте переменным осмысленные имена.

function createListItem(text) {
  const li = document.createElement('li');
  li.textContent = text;
  return li;
}

const list = document.querySelector('ul');
list.appendChild(createListItem('Новый пункт'));

Эти принципы просты, но именно они делают код живых интерфейсов лёгким, быстрым и понятным. DOM-операции остаются под контролем, страница не тормозит, а разработка превращается не в борьбу с браузером, а в аккуратную настройку его поведения.

Больше интересного про код — в нашем телеграм-канале.  Подписывайтесь!




Курс с трудоустройством: «Профессия Фронтенд-разработчик + ИИ» Узнать о курсе
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована