Узлы DOM — гэта не рэштры HTML, якія вы адправляеце.
DOM – гэта не тэкст HTML, які вы аплывалі. Дрэвы парсавання, жывыя NodeLists і перэраспалювання елементаяў пояснююць, чаму падход, які базуецца на тэкстовым роўні, не працуе ў фронтэндах на JavaScript і React.
Крыніцы: The DOM Is Not Your HTML: The Difference Every Frontend Developer Should Understand
Вы пішаеце HTML, але JavaScript ніколі не выконвае змян у тым файле на дыску. Ён взаімаеся з жывой модэллю об’екта дакумента браузера.
Калі маркап прыходзіць да браузера, ён стварае пачатковую структуру сторанкі. Дакумент можа пачынацца простым заголовкам:
<h1>Hello</h1>
Пасля завантажэння JavaScript можа знайсці гэты заголовак і заменіць яго тэкст:
document.querySelector("h1").textContent = "Hello World";
Потым на экране паказваецца:
<h1>Hello World</h1>
Можа здавацца, што сам файл HTML быў перапісан. Аднак гэтага не сталося. Файл на серверэ все ўтрымвае первісны заголовак. JavaScript змяніў жывую модэль сторанкі ў браузеры — DOM. Раз’ядрэнне выхіднага маркапу ад гэтай жывой модэлі паводзіць да яснасці багацых аспектаў працы JavaScript.
HTML — гэта дакумент. DOM — жывая рэпрэзентацыя.
HTML — гэта маркап, які вы пішаеце, напрыклад:
<body>
<h1>Hello</h1>
<button>Click me</button>
</body>
Браузер перакладвае гэты документ у структуру ў памяці. Гэтая структура — гэта Мадэль об’екта документа. Апроставаныя етапы перадачы выглядаюць так:
HTML
│
│ browser parses
▼
DOM
│
├── body
│ ├── h1
│ └── button
DOM — гэта не проста копія вашага HTML у віглядзе строкі. Цэў структураваны, програмавальны документ. Самэ гэта і маніпулюе JavaScript.
Тады што на самай працэ ўсё-такі зменяе JavaScript?
Пачніце з:
<h1 id="title">Hello</h1>
Потым запускайце:
const title = document.querySelector("#title");
title.textContent = "Welcome";
Скрыпт знаходзіць вузол DOM і аднавляе яго тэкст. Цяпер «жывое» дрэва выражае:
<h1 id="title">Welcome</h1>
Авангародны HTML не быў перапісаны. Калі маркап выйшаў з сервера або статычнага файла, гэты ісходны файл все ўсё містыць:
<h1 id="title">Hello</h1>
Разлік маленькі, але важны: змяненне DOM не ўзначае рэдагавання ісходнаг HTML.
Этап просты спосаб разгледзець разлік
Ачыніце сторанку і зменіце ўсё-што трэба з консолі:
document.querySelector("h1").textContent = "Changed!";
Відобразаемая сторынка апдэйтаваецца немеджліва. Порашчырайце жывой DOM:
<h1>Changed!</h1>
з аднорыжным HTML-кодам:
<h1>Hello</h1>
Яны можаць разлічвацца, таму што браузер працуе з жывым DOM.
DOM — гэта не толькі тое, што вы спачатку напісалі
Падазроўваем, што маркап начынаецца так:
<ul id="users">
<li>John</li>
</ul>
JavaScript можа дадаць ўтварэнне ўсё новага элемента:
const list = document.querySelector("#users");
const item = document.createElement("li");
item.textContent = "Sarah";
list.appendChild(item);
Тады жывая структура будзе выглядаць так:
ul
├── li "John"
└── li "Sarah"
„Sarah“ ніколі не была ў аднорыжным файле. Браузер не выклікае патрэбы перапісваць даследны документ; скрыпта зменіла жывы DOM. Гэтая можлівасць ўтрамбавана ў інтэрактыўныя сайты.
DOM — гэта дрэва
„Object Model“ звучыць сложней, чым ён на самай працоўцы. У сваёй простай форме DOM — гэта дрэва з вузламі. Як толькі:
<main>
<h1>Hello</h1>
<p>Welcome!</p>
</main>
браузер стварае структуру прыбліжна такую:
Document
│
└── main
├── h1
│ └── "Hello"
│
└── p
└── "Welcome!"
Сама структура дрэва ўможлівяе скрыптам аналізаваць такія зв’язкі:
element.parentElement
element.children
element.nextElementSibling
Вы не скануеце файл з простым тэкстам; вы навігуеце па структураваным дрэве.
І DOM можа змінювацца
Выхідны HTML зазвычай ёст тэкст документа. Дрэва, якія знаходзяцца ў памяці, залішаюцца зменнымі пад час выканання. Скрыпты можа прызначаць вузлы:
const button = document.createElement("button");
задаваць атрыбуты:
button.setAttribute("disabled", "");
змінюваць контэнт:
button.textContent = "Loading...";
і дадаваць вузел:
document.body.append(button);
Дрэва развіваецца пад час працы прыкладнага програма, і самэ гэта ўскладнюе стварэння інтэрактыўных інтэрфейсаў, якім не патрэбна загрузка цэлага HTML-дакумента за кожную незначную змяну.
Але ёсць ўсё ж адна рэч, яку развівачы часта плутаюць
Аналіз сторанкі можа адкрыць HTML, якога няма ў первісным выхідным кодзе. Скрыпт можа стварыць:
<div class="notification">
Saved successfully
</div>
Вы не створылі той вузел, пры тым DevTools можа паказаць яго ў панелі Elements, таму што гэтая панель відбівае DOM. Функцыя View Source можа аж усё жоўстка прыховаць гэты вузел.
Таму не ёсць праблемай, што рэзультаты функцый View Source і Inspect Element не завжды будуць аднаковымі. Адна з распашчых прычын — гэта тое, што JavaScript мутуюць DOM пасля першага завантажэння HTML.
Чаму гэта важна для распрацоўшчыкаў фронтэнду?
Большасць поведання фронтэнду стае яснейшым, калі ментальная мадэль зменяецца з “JavaScript зменяе HTML” на “JavaScript зменяе DOM”. Такая парадыгма дапамагае зрозумець такія API, як:
element.classList.add("active");
element.remove();
element.textContent = "Loading...";
element.append(newElement);
Гэтыя вызовы працуюць з актуальной структурой дакумента. Браузер пасля таго выкарыстоўвае гэтую апдэйтаваную структуру пад час відрысовкі экрана.
А як ў разы з React?
Тая ж сама розмежованасць дапамагае пад час вучэння React. Вы моглі бы напісаць:
return <h1>Hello</h1>;
JSX – гэта не сам DOM. Гэта сынтаксіс, які опісвае інтэрфейс корыстніка. React выбірае, як гэты опіс павінен быць пераданы ў рэальны DOM. Розумеўшы DOM перш чым прыступаць да абстракцый фрэймворка, рэндарынг, супрацоўка з дадзенымі і абнаўленні DOM становяцца менш загадковымі.
Вам не патрэбны всі внутранія дэталі. Памятайце прыступ да працы з дадзенымі:
HTML
↓
Browser parses it
↓
DOM
↓
JavaScript can modify it
↓
Browser updates the rendered page
Фрэймворкі дадаюць дапаможныя слоі, але базой застаёцца DOM браузера.
Адна распашчастая памылка
Распрацоўшчыкі інодзе спрыяжуць мутацыю DOM з постоянным зменам первіснага HTML. Гэта не так. Напрыклад:
document.body.innerHTML = "<h1>New page</h1>";
Эта прытласць можа заменіць чынную структуру, але не перапісвае файл HTML на серверы. Актуалізацыя перазавантажвае дакумент з яго вучэння. Як толькі змяны не былі зафіксаваны і не быў створаны новы HTML, вяртаецца первісны дакумент. Гэты разлік аддзеля стан пад час выканання ад вучэння кантэнту.
Мадэль мышлення, якую трэба памятаць
Утрымайцеся ад сплывання ідэй:
HTML = DOM
Валіце варыянт, бліжэйшы да:
HTML
│
│ parsed by browser
▼
DOM
│
│ modified by JavaScript
▼
Updated DOM
│
│ used during rendering
▼
What the user sees
Спрыяйце HTML як дакумент-вучэнне, створаны автаром. Спрыяйце DOM як змінныя дрэва браузера, створанага на адной з гэтых вучэнняў. Скрыпты зміняюць гэтае дрэва; адрысаванне выконвалія паўтарае тое, што ў дрэве ёсць у даны момант.
Заключны вывад
Не плутайце DOM з файлам HTML. Маркап — гэта тое, што вы пісваеце; модэль об’екта — гэта тое, што браузер падтрымвае паказаным, калі вкладка ачытаеся. З такым разлікам калькія аспекты стаюць зрозумелымі: чаму скрыпты можаюць дадаваць і адчыняць вузлы, чаму querySelector() функцыонуе, чаму textContent адкорэгуе тое, што вы бачыце, чаму інструменты развіцця можаюць паказваць вузлы, якія вы ніколі не пісвалі, чаму «жывое дрэва» можа адрозніцца ад вачымоўкі, і чаму React такім чынам усё-такі вплывае на DOM.
Корыстны падход прост: вы пісваеце HTML, браузер стварае DOM, а JavaScript зміняе DOM. Калі гэта стане зрозумелым, DOM перастае выглядаць як незрозумелы API і становіцца тым, чым ён ёсць — жывым адпрацоўванням стораніцы браузерам.