Skip to content

Commit c17d32d

Browse files
authored
Apply suggestions from code review
1 parent d7c87ad commit c17d32d

File tree

1 file changed

+28
-27
lines changed

1 file changed

+28
-27
lines changed

8-web-components/7-shadow-dom-events/article.md

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# Тіньовий DOM та події
22

3-
Головна мета створення тіньового дерева це інкапсуляція внутрішньої реалізації компоненту.
3+
Головна мета створення тіньового дерева -- це інкапсуляція внутрішньої реалізації компоненту.
44

5-
Скажімо, було виконано подію click всередині тіньового DOM компоненту `<user-card>`. Але ж скріпти в головному документі і гадки не мають про внутрішню будову тіньового DOM, особливо, якщо компонент походить зі сторонньої бібліотеки. Отже, для збереження інкапсуляції вмісту, браузер *змінює у цієї події цільовий елемент*.
5+
Уявімо, користувач клікнув на якийсь елемент всередені тіньового DOM компоненту `<user-card>`, і відбулася подія click. Але ж скріпти в головному документі і гадки не мають про внутрішню будову тіньового DOM, особливо, якщо компонент походить зі сторонньої бібліотеки.
66

7-
**Події, що відбуваються у тіньовому DOM, впливають на батьківський елемент, навіть якщо відбулися за межами компоненту.**
7+
Отже, для збереження інкапсуляції вмісту, браузер *змінює у цієї події цільовий(target) елемент*.
8+
9+
**Події, що відбуваються у тіньовому DOM, мають його "host" у властивості `target` об'єкту події, якщо подія обробляється за межами компоненту.**
810

911
Розглянемо простий приклад:
1012

@@ -19,26 +21,25 @@ customElements.define('user-card', class extends HTMLElement {
1921
<button>Click me</button>
2022
</p>`;
2123
this.shadowRoot.firstElementChild.onclick =
22-
e => alert("Внутрішній цільовий елемент: " + e.target.tagName);
24+
e => alert("target зсередини: " + e.target.tagName);
2325
}
2426
});
2527
2628
document.onclick =
27-
e => alert("Зовнішній цільовий елемент: " + e.target.tagName);
29+
e => alert("target ззовні: " + e.target.tagName);
2830
</script>
2931
```
3032

3133
Клікнувши на кнопку, отримаємо наступні повідомлення:
3234

33-
1. Внутрішній цільовий елемент: `BUTTON` – внутрішній обробник подій отримує правильну ціль – елемент всередині тіньового DOM
34-
2. Зовнішній цільовий елемент: `USER-CARD` – обробник подій документу отримує тіньовий хост в якості цільового елементу
35-
35+
1. target зсередини: `BUTTON` -- внутрішній обробник подій отримує правильний target -- елемент всередині тіньового DOM.
36+
2. target ззовні: `USER-CARD` -- обробник подій документу отримує тіньовий хост в якості target події.
3637

37-
Зміна цільового елементу – чудова річ, тому що зовнішній документ не повинен знати про внутрішній вміст компоненту. З цієї точки зору, подія відбулась в `<user-card>`.
38+
Зміна target події -- чудова річ, бо зовнішній документ не повинен знати про внутрішній вміст компоненту. З цієї точки зору, подія відбулась в `<user-card>`.
3839

39-
**Зміна цільового елементу не відбувається, якщо подія починається з елементу зі слота, що фактично знаходиться в звичайному світлому DOM.**
40+
**Зміна target не відбувається, якщо подія починається з елементу зі слоту, що фактично знаходиться в звичайному світлому DOM.**
4041

41-
Наприклад, якщо користувач клікає на `<span slot="username">` у прикладі, наведеному нижче, цільовим елементом є саме цей `span` елемент, для обох обробників звичайного (світлого) та тіньового:
42+
Наприклад, якщо користувач клікає на `<span slot="username">` у прикладі, наведеному нижче, цільовим елементом є саме цей елемент `span`, для обох обробників -- звичайного (світлого) та тіньового:
4243

4344
```html run autorun="no-epub" untrusted height=60
4445
<user-card id="userCard">
@@ -56,25 +57,25 @@ customElements.define('user-card', class extends HTMLElement {
5657
</div>`;
5758
5859
this.shadowRoot.firstElementChild.onclick =
59-
e => alert("Внутрішній цільовий елемент: " + e.target.tagName);
60+
e => alert("target зсередини: " + e.target.tagName);
6061
}
6162
});
6263
63-
userCard.onclick = e => alert(`Зовнішній цільовий елемент: ${e.target.tagName}`);
64+
userCard.onclick = e => alert(`target ззовні: ${e.target.tagName}`);
6465
</script>
6566
```
6667

67-
Якщо клік відбувся на `"Іван Коваль"`, для обох внутрішнього та зовнішнього обробників цільовим елементом є `<span slot="username">`. Так як це елемент зі світлого DOM, то зміни цільового елементу не відбувається.
68+
Якщо клік відбувся на `"Іван Коваль"`, для обох -- внутрішнього та зовнішнього -- обробників у target буде елемент `<span slot="username">`. Це елемент зі світлого DOM, тому зміна target не відбувається.
6869

69-
З іншого боку, якщо клік відбувся на елементі з тіньового DOM, т.я.`<b>Name</b>`, тоді він вспливає з тіньового DOM, a його цільовим елементом `event.target` стає `<user-card>`.
70+
З іншого боку, якщо клік відбувся на елементі з тіньового DOM, напр. на `<b>Name</b>`, то коли він вспливає з тіньового DOM, його `event.target` стає `<user-card>`.
7071

7172
## Спливання, event.composedPath()
7273

73-
Для цілей спливання подій (бульбашковий механізм) використовується розгорнутий DOM.
74+
Для реалізації спливання подій (бульбашковий механізм) використовується підхід розгорнутого DOM.
7475

75-
Отже, якщо у нас є елемент у слоті, і подія відбувається десь усередині цього елементу, тоді вона підіймається до `<slot>` і вище.
76+
Отже, якщо у нас є елемент у слоті, і подія відбувається десь всередині цього елементу, тоді вона підіймається до `<slot>` і вище.
7677

77-
Повний шлях до початкового цільового елементу з усіма тіньовими елементами можна отримати за допомогою `event.composedPath()`. Як видно з назви методу, він повертає шлях після композиції.
78+
Повний шлях до справжнього target елементу цієї події, включаючи всі тіньові елементи, можна отримати за допомогою `event.composedPath()`. Як видно з назви методу, він повертає шлях після складання всіх його елементів.
7879

7980
У наведеному вище прикладі зведений DOM виглядає так:
8081

@@ -91,7 +92,7 @@ userCard.onclick = e => alert(`Зовнішній цільовий елемен
9192
```
9293

9394

94-
Отже, для кліку по `<span slot="username">` виклик `event.composedPath()` повертає масив: [`span`, `slot`, `div`, `shadow-root`, `user-card`, `body`, `html`, `document`, `window`], що цілковито відображає батьківський ланцюжок, починаючи з цільового елемента у зведеному DOM після композиції.
95+
Отже, для кліку по `<span slot="username">` виклик `event.composedPath()` повертає масив: [`span`, `slot`, `div`, `shadow-root`, `user-card`, `body`, `html`, `document`, `window`], що цілковито відображає батьківський ланцюжок, починаючи з target елемента у зведеному DOM після складання.
9596

9697
```warn header="Деталі тіньового дерева надаються лише для дерев з `{mode:'open'}`"
9798
Якщо тіньове дерево було створено з `{mode: 'closed'}`, то тоді складений (composed) шлях починається від хоста: `user-card` і вище.
@@ -102,9 +103,9 @@ userCard.onclick = e => alert(`Зовнішній цільовий елемен
102103
103104
## Властивість event.composed
104105
105-
Більшість подій успішно проходять через тіньову межу DOM. Є кілька подій, які цього не роблять.
106+
Більшість подій успішно проходять через тіньову межу DOM. Є кілька подій, які нездатні на це.
106107
107-
Це регулюється властивістю об’єкта події `composed`. Якщо це `true`, то подія дійсно перетинає межу. В іншому випадку його можна буде перехопити лише зсередини тіньового DOM.
108+
Це регулюється властивістю об’єкта події `composed`. Якщо вона `true`, то подія дійсно може перетнути межу. В іншому випадку її можна буде перехопити лише зсередини тіньового DOM.
108109
109110
Якщо ви подивитесь на [UI Events specification](https://www.w3.org/TR/uievents), більшість подій мають `composed: true`:
110111
@@ -118,16 +119,16 @@ userCard.onclick = e => alert(`Зовнішній цільовий елемен
118119
119120
Та існують деякі події, що мають `composed: false`:
120121
121-
- `mouseenter`, `mouseleave` (ці події взагалі не спливають вгору),
122+
- `mouseenter`, `mouseleave` (ці події взагалі не вспливають),
122123
- `load`, `unload`, `abort`, `error`,
123124
- `select`,
124125
- `slotchange`.
125126
126-
Ці події можна перехопити лише на елементах у межах того ж самого DOM, де знаходиться цільовий елемент події.
127+
Ці події можна перехопити лише на елементах у межах того ж самого DOM, де знаходиться target елемент події.
127128
128-
## Генерація подій (сustom events)
129+
## Генерація подій (Custom events)
129130
130-
Коли ми генеруємо користувацькі події, нам потрібно встановити для властивостей `bubbles` і `composed` значення `true`, щоб вони спливали та виходили за межі компонента.
131+
Коли ми генеруємо користувацькі події, нам потрібно встановити для властивостей `bubbles` і `composed` значення `true`, щоб вони вспливали та виходили за межі компонента.
131132
132133
Наприклад, тут ми створюємо `div#inner` у тіньовому DOM `div#outer` і запускаємо дві події для нього. Лише та, що має `composed: true`, виходить за межі документа:
133134
@@ -168,13 +169,13 @@ inner.dispatchEvent(new CustomEvent('test', {
168169

169170
## Підсумки
170171

171-
Лише ті події перетинають тіньові межі DOM, для прапорця `composed` яких встановлено значення `true`.
172+
Лише ті події перетинають тіньові межі DOM, у прапорці `composed` яких задано значення `true`.
172173

173174
Вбудовані події здебільшого мають `composed: true`, як описано у відповідних специфікаціях:
174175

175176
- Події інтерфейсу користувача (UI Events) <https://www.w3.org/TR/uievents>.
176177
- Сенсорні події (Touch Events) <https://w3c.github.io/touch-events>.
177-
- Події курсору (Pointer Events) <https://www.w3.org/TR/pointerevents>.
178+
- Події вказівника (Pointer Events) <https://www.w3.org/TR/pointerevents>.
178179
- ...тощо.
179180

180181
Деякі вбудовані події, що мають `composed: false`:

0 commit comments

Comments
 (0)