Галоўная / Артыкулы

Узлы DOM — гэта не рэштры HTML, якія вы адправляеце.

DOM – гэта не тэкст HTML, які вы аплывалі. Дрэвы парсавання, жывыя NodeLists і перэраспалювання елементаяў пояснююць, чаму падход, які базуецца на тэкстовым роўні, не працуе ў фронтэндах на JavaScript і React.

Выдавец: REACTAPP.TOP5 хв чытання1180 слоў

Крыніцы: 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 і становіцца тым, чым ён ёсць — жывым адпрацоўванням стораніцы браузерам.