DOM vs HTML: в чём разница
HTML — текстовый язык разметки, описывающий структуру документа; DOM (Document Object Model) — живое дерево объектов в памяти браузера, построенное на основе HTML и доступное для чтения и изменения через JavaScript.
Зачем нужно
Понимание разницы между HTML и DOM объясняет многие неожиданные поведения: почему innerHTML возвращает не то, что написано в файле; почему браузер «исправляет» невалидный HTML; почему JavaScript-изменения не меняют исходный HTML-файл. Это фундаментальное знание для всех, кто работает с DOM API.
Где используется
- Понимание работы DevTools: вкладка Elements показывает DOM, а не HTML-файл
- Объяснение браузерной корректировки:
<table>без<tbody>— браузер добавит сам в DOM - Динамическое изменение страницы без перезагрузки: работа с DOM API
- Понимание разницы между
innerHTML(сериализация DOM) и исходным HTML - Объяснение, почему
document.write(запись в HTML-поток) опасен
Основной контент
HTML — статический текст
<!-- Это HTML — текстовый файл на диске -->
<!DOCTYPE html>
<html>
<head>
<title>Моя страница</title>
</head>
<body>
<p>Привет, мир!</p>
<!-- Намеренно невалидный HTML: таблица без tbody -->
<table>
<tr><td>Данные</td></tr>
</table>
</body>
</html>
DOM — живое дерево объектов
// DOM — это не просто текст, а объекты с методами и свойствами
const p = document.querySelector('p');
// p — это объект HTMLParagraphElement
console.log(p instanceof HTMLElement); // true
console.log(p instanceof Node); // true
console.log(p.tagName); // 'P'
console.log(p.textContent); // 'Привет, мир!'
console.log(p.nodeType); // 1 (ELEMENT_NODE)
// DOM позволяет изменять страницу без перезагрузки
p.textContent = 'Изменено через DOM!';
p.style.color = 'red';
p.classList.add('highlighted');
// Исходный HTML-файл не изменился — изменилась только память браузера
Браузер «исправляет» HTML при построении DOM
// Невалидный HTML (без tbody) — браузер сам добавит tbody в DOM
// <table><tr><td>...</td></tr></table>
const table = document.querySelector('table');
const tbody = table.querySelector('tbody');
console.log(tbody); // HTMLTableSectionElement — хотя в HTML его нет!
// Вложенность тегов исправляется:
// <p><div></div></p> → браузер закроет <p> до <div>
// Незакрытые теги браузер закрывает сам:
// <p>Текст → в DOM это <p>Текст</p>
Соотношение DOM и HTML
// serialization: DOM → HTML строка
const div = document.createElement('div');
div.innerHTML = '<p>Привет</p>';
console.log(div.outerHTML); // '<div><p>Привет</p></div>'
// innerHTML — сериализация текущего состояния DOM, не исходный HTML
// parsing: HTML строка → DOM узлы
const parser = new DOMParser();
const doc = parser.parseFromString('<p>Текст</p>', 'text/html');
const pNode = doc.querySelector('p');
console.log(pNode.textContent); // 'Текст'
// Типы узлов DOM (nodeType)
// 1 — Element
// 3 — Text
// 8 — Comment
// 9 — Document
// 11 — DocumentFragment
document.body.childNodes.forEach(node => {
console.log(node.nodeType, node.nodeName);
});
Динамические изменения DOM
// DOM живёт в памяти и обновляется в реальном времени
const list = document.getElementById('items');
// Добавление элемента
const li = document.createElement('li');
li.textContent = 'Новый пункт';
list.appendChild(li);
// Удаление элемента
const first = list.firstElementChild;
first.remove();
// DOM отражает эти изменения немедленно — пользователь видит обновление
// HTML-файл на сервере при этом не изменился
Частые ошибки
- «Правлю HTML — ничего не меняется»: изменение исходного HTML-файла не влияет на уже загруженный DOM. Нужно перезагрузить страницу.
- «DOM === HTML»: DOM строится из HTML, но браузер добавляет узлы (tbody, html, head, body), исправляет ошибки. Они не идентичны.
- Использование
innerHTMLдля получения «исходного» HTML:innerHTMLвозвращает сериализованный DOM, а не то, что написано в файле — могут быть различия в кавычках, атрибутах, порядке. document.writeпосле загрузки: вызов после загрузки страницы перезаписывает весь DOM, удаляя текущее содержимое.