Бравзер прощає дуже багато помилок вашому HTML-документу.
Зумовлено це, в першу чергу, принципом зворотної сумісності — веб повинен працювати, навіть якщо ваш документ — дрімуче легасі. І для цього бравзер змушений "додумувати" елементи, щоб побудувати валідне DOM-дерево з того, що ви йому прислали.
Правил для такої поблажливості існує дуже багато, тож я не буду намагатися розказати про всі, а лишень огляну основні принципі такої "незламності".
Ви можете написати в .html файлі просто:
Hello, world!
і бравзер перетворить це на:
#el(html)
#el(head)
#el(body)
#text("Hello, world!")
Бо DOM-дерево мусить мати кореневий елемент, мусить мати
head і
body. І йому простіше їх самому вставити, ніж драматично падати під час парсингу.
Важливий момент — бравзер при цьому нічого не робить саме з вашим
.html. Він бере його за основу для побудови DOM, з якого вже в результаті утворюється відображення документу у вікні переглядача.
Так от. У бравзера є багато правил, за якими він обробляє ваш
html, і суттєва їх кількість забезпечує певні "неявні" перетворення.
Наприклад, необовʼязкові закриваючі теги. В певних випадках ви можете не писати їх через те, як певні елементи взаємодіють між собою. Візьмемо приклад:
<p>Hello
<p>World
Бравзер явно перетворить це на два параграфи:
#el(p)
#text("Hello")
#el(p)
#text("World")
Чому? Бо параграф не може містити блочні елементи всередині себе, тому, зустрічаючи відкриваючий тег для блочного елементу після відкриваючого
p, він робить висновок, що попередній елемент закінчився, і його пора закрить. До речі, він застосує таке виправлення, коли вам заманеться покласти
div всередину
p:
<p>
<div></div>
</p>
Це перетвориться на:
#el(p)
#el(div)
#el(p)
Хід "думок" приблизно такий:
"так, ось параграф, шо там далі? О, div, значить p треба закрить. Так, div закінчився, шо там далі? Закриваючий p? А шо він закриває? Непорядок! У мене попередньо все закрите, тому тут буде просто параграф. Пустий."До речі, просто текст бравзер не перетворює автоматично на параграф, а створює текстовий вузол, який цілком собі є повноправним учасником DOM.
І так, бравзер не вгадує, що ви там мали на увазі. У нього є дуже чіткі правила і механізми "відновлення", за якими він будує DOM так, щоб мати можливість відобразити бодай щось.
Якщо розмітка неповна, суперечлива або просто написана не так, як очікує парсер, бравзер не зупиняється. Він намагається зібрати з цього валідну внутрішню структуру документа. Десь він неявно створює бракуючі елементи, десь закриває відкриті раніше, десь переносить вузли в інше місце, десь ігнорує те, що не може нормально вбудувати в дерево.
Тому памʼятайте, якщо щось є в
html, але чогось не видно в DOM, або воно не на тому місці, то з вірогідністю, що наближається до 100%, це діло ваших непосидючих рук, а не баг бравзера.
В цьому немає нічого містичного чи непередбачуваного, усі аномалії в DOM зʼявляються чітко згідно внутрішніх правил.
До речі, так було не завжди, і свого часу була спроба запровадити стандарт XHTML, який передбачав дуже строгий синтаксис, і бравзер дуже драматично падав від помилок парсингу. Але це нікому не сподобалося чомусь, і стандарт не прижився. Натомість ми маємо HTML5, що і друзів розуміє, і ворогів уміє прощати. Ну себто ваш кучерявий і неправильний HTML.
Якщо коротко: бравзер має величезну кількість правил, трюків, виключень і алгоритмів, щоб змусити ваш HTML-документ відобразитися у вікні переглядача бодай якось замість звалитися з помилкою. Бо сторінка з 1991 року має відображатися і в 2026 році попри все.
Ось такий веб дбайливий щодо свого спадку.
@babichdev