Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Shadow DOM проти звичайного DOM». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Звичайний (light) DOM, це спільне дерево документа, а Shadow DOM, це вкладене ізольоване дерево всередині елемента-хоста зі своїми стилями, розміткою й поведінкою.** Light DOM доступний усьому коду через `document.querySelector()` і повністю підпорядкований глобальним CSS. Shadow DOM живе всередині host-елемента, доступний лише через `element.shadowRoot`, і глобальні стилі туди не дістають; вміст ззовні підставляють у нього через `<slot>`. Саме тому на Shadow DOM будують Web Components і вбудовані елементи на кшталт `<video>` чи `<input type="range">`. ```javascript document.querySelector('my-card').innerHTML; // тільки «оболонка» document.querySelector('my-card').shadowRoot.querySelector('button'); // внутрішня кнопка ``` **Ключове:** light DOM спільний для всієї сторінки, Shadow DOM, це окремий «міні-світ» усередині елемента з власними стилями та власною точкою доступу.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Звичайний (light) DOM, це основне дерево документа, а Shadow DOM, це вкладене ізольоване DOM-дерево всередині елемента, яке має свої стилі, розмітку й поведінку і не впливає на зовнішній документ.** Простими словами: light DOM, це «поверхня сайту», а Shadow DOM, це «нутрощі компонента», сховані від решти світу. ## Теорія ### TL;DR - Light DOM, це все, що ви пишете в HTML і бачите в DevTools; Shadow DOM живе всередині host-елемента. - Light DOM підпорядкований глобальним CSS, Shadow DOM від них ізольований. - До light DOM ходять через `document.querySelector()`, до тіньового, тільки через `shadowRoot.querySelector()`. - Слоти (`<slot>`) є лише в Shadow DOM: через них вміст із light DOM показується всередині компонента. - Shadow DOM потрібен для інкапсуляції компонентів, ізоляції стилів і самодостатніх елементів на кшталт `<video>`. ### Швидкий приклад ```javascript const card = document.querySelector('.card'); const shadow = card.attachShadow({ mode: 'open' }); shadow.innerHTML = ` <style> button { color: blue; } </style> <button>Внутрішня</button> `; ``` Тепер у `card` є внутрішній Shadow DOM, який ізольований від зовнішніх стилів, не видно у звичайному дереві документа (а при `mode: 'closed'` до нього взагалі не дістатися ззовні) і який керує власними елементами. Глобальне правило `button { color: red; }` не перефарбує цю кнопку, бо вона живе у своєму тіньовому дереві. ### Означення: light DOM і Shadow DOM | Тип | Що це таке | | --- | --- | | **Звичайний (light) DOM** | Основне дерево HTML-документа: усе, що ви пишете в HTML і бачите в DevTools. | | **Shadow DOM** | Вкладене ізольоване DOM-дерево всередині елемента (компонента), яке має свої стилі, розмітку й поведінку і не впливає на зовнішній документ. | Для порівняння, звичайна картка в light DOM: ```html <div class="card"> <h2>Заголовок</h2> <button>Клік</button> </div> ``` Тут усе видно в основному DOM, кнопку можна дістати через `document.querySelector('button')`, а глобальні стилі впливають на все: правило `button { color: red; }` зробить кнопку червоною. ### Ключові відмінності | Характеристика | **Звичайний DOM (light)** | **Shadow DOM** | | --- | --- | --- | | Де живе | У дереві документа | Усередині host-елемента | | Видимість у DevTools | Так | Так, але приховано за замовчуванням, вмикається опцією «Show user agent shadow DOM» | | Стилізація | Підпорядкований глобальним CSS | Ізольований від глобальних стилів | | Ізоляція | Немає, стилі та JS впливають одне на одного | Повна, ізольований контекст | | Де використовують | Будь-який HTML | Web Components, вбудовані елементи (`<video>`, `<input>` та інші) | | Взаємодія | Прямо через `document.querySelector()` | Тільки через `shadowRoot.querySelector()` | | Слоти | Немає | Є (`<slot>` для вставки вмісту з light DOM) | ### Структура і слоти ```html <body> <my-card> <!-- host-елемент --> #shadow-root (shadow DOM) <style>...</style> <button>Click</button> #light-dom (звичайний DOM) <p slot="content">...</p> </my-card> </body> ``` Тут `my-card`, це елемент-хост, `#shadow-root`, це його внутрішній Shadow DOM, а зовнішні елементи можуть передавати вміст усередину через `<slot>`. Важливо: слотований `<p>` фізично залишається в light DOM і підпадає під глобальні стилі, просто показується у визначеному місці тіньового дерева. ### Доступ із коду Із light DOM видно лише «оболонку» компонента: ```javascript console.log(document.querySelector('my-card').innerHTML); // Порожньо, бо нутрощі заховані в Shadow DOM ``` До тіньового дерева ходять лише за посиланням на корінь: ```javascript const shadow = document.querySelector('my-card').shadowRoot; console.log(shadow.querySelector('button')); // <button>Click</button> ``` Якщо дерево створене з `mode: 'closed'`, `shadowRoot` буде `null`, і ззовні до внутрішніх вузлів не дістатися взагалі. ### Де це потрібно на практиці Shadow DOM беруть, коли треба: - інкапсулювати компонент (веб-компоненти, UI-бібліотеки); - ізолювати стилі, щоб глобальні CSS не ламали зовнішній вигляд; - зробити перевикористовуваний елемент, наприклад `<custom-slider>`; - зробити компонент самодостатнім, як `<video>` чи `<select>`. Життєвий приклад. У звичайному DOM ви перевизначили стилі кнопок на всьому сайті, і раптом кнопки всередині компонента «поламалися», бо стилі глобальні. З Shadow DOM компонент зі своїми стилями ізольований, і жодне глобальне `button { color: red }` на нього не вплине. | Критерій | **Звичайний DOM** | **Shadow DOM** | | --- | --- | --- | | Видимість | Доступний усьому коду | Прихований і ізольований | | Стилі | Спільні | Ізольовані | | Використання | Повсякденний HTML | Компоненти і вбудовані елементи | | Взаємодія | Через `document` | Через `shadowRoot` | | Безпека | Ізоляції немає | Є інкапсуляція | ### Типові помилки - Шукати внутрішні вузли компонента через `document.querySelector()`: селектор не перетинає межу тіньового дерева. - Думати, що глобальний CSS «просто спрацює» всередині компонента: туди стилі передають через власний `<style>`, CSS-змінні або `::part`. - Плутати слот із перенесенням вузла: слотований вміст лишається в light DOM, а не копіюється в тіньове дерево. - Вважати `closed` захистом даних: це лише ускладнення доступу, реальної безпеки воно не дає. - Забувати про події: щоб подія з тіньового дерева піднялася назовні, її створюють з `composed: true`, і ззовні її `target` буде хостом.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.