You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 2-ui/2-events/02-bubbling-and-capturing/article.md
+7-9Lines changed: 7 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -108,9 +108,9 @@
108
108
109
109
Например:
110
110
111
-
1. Мы делаем вложенное меню. Каждое подменю обрабатывает клики на своих элементах и делает для них `stopPropagation`, чтобы не открывать внешнее меню.
111
+
1. Мы делаем вложенное меню. Каждое подменю обрабатывает клики на своих элементах и делает для них `stopPropagation`, чтобы не срабатывало внешнее меню.
112
112
2. Позже мы решили отслеживать все клики в окне для какой-то своей функциональности, к примеру, для статистики – где вообще у нас кликают люди. Некоторые системы аналитики так делают. Обычно используют `document.addEventListener('click'…)`, чтобы отлавливать все клики.
113
-
3. Наша аналитика не будет работать над областью, где клики прекращаются `stopPropagation`. Получилась «мёртвая зона».
113
+
3. Наша аналитика не будет работать над областью, где клики прекращаются `stopPropagation`. Увы, получилась "мёртвая зона".
114
114
115
115
Зачастую нет никакой необходимости прекращать всплытие. Задача, которая, казалось бы, требует этого, может быть решена иначе. Например, с помощью создания своего уникального события, о том, как это делать, мы поговорим позже. Также мы можем записывать какую-то служебную информацию в объект `event` в одном обработчике, а читать в другом, таким образом мы можем сообщить обработчикам на родительских элементах информацию о том, что событие уже было как-то обработано.
116
116
```
@@ -134,7 +134,7 @@
134
134
135
135
**Ранее мы говорили только о всплытии, потому что другие стадии, как правило, не используются и проходят незаметно для нас.**
136
136
137
-
Обработчики, добавленные через `on<event>`-свойство или через HTML-атрибуты, или через `addEventListener(event, handler)`, ничего не знают о фазе погружения, а работают только на 2-ой и 3-ей фазах.
137
+
Обработчики, добавленные через `on<event>`-свойство или через HTML-атрибуты, или через `addEventListener(event, handler)` с двумя аргументами, ничего не знают о фазе погружения, а работают только на 2-ой и 3-ей фазах.
138
138
139
139
Чтобы поймать событие на стадии погружения, нужно использовать третий аргумент `capture` вот так:
````smart header="На каждой фазе разные обработчики на одном элементе срабатывают в порядке назначения"
193
-
Если у нас несколько обработчиков одного события, назначенных `addEventListener` на один элемент, в рамках одной фазы их порядок срабатывания - тот же, в котором они установлены:
193
+
Если у нас несколько обработчиков одного события, назначенных `addEventListener` на один элемент, в рамках одной фазы, то их порядок срабатывания - тот же, в котором они установлены:
194
194
195
195
```js
196
196
elem.addEventListener("click", e => alert(1)); // всегда сработает перед следующим
@@ -200,11 +200,9 @@ elem.addEventListener("click", e => alert(2));
200
200
201
201
## Итого
202
202
203
-
Алгоритм:
203
+
При наступлении события - самый глубоко вложенный элемент, на котором оно произошло, помечается как "целевой" (`event.target`).
204
204
205
-
При наступлении события -- элемент, на котором оно произошло, помечается как "целевой" (`event.target`).
206
-
207
-
- Событие сначала двигается вниз от корня документа к `event.target`, по пути вызывая обработчики, поставленные через `addEventListener(...., true), где `true` -- это сокращение для `{capture: true}`.
205
+
- Затем событие сначала двигается вниз от корня документа к `event.target`, по пути вызывая обработчики, поставленные через `addEventListener(...., true), где `true` -- это сокращение для `{capture: true}`.
208
206
- Далее обработчики вызываются на целевом элементе.
209
207
- Далее событие двигается от `event.target` вверх к корню документа, по пути вызывая обработчики, поставленные через `on<event>` и `addEventListener` без третьего аргумента или с третьим аргументом равным `false`.
210
208
@@ -222,4 +220,4 @@ elem.addEventListener("click", e => alert(2));
222
220
223
221
Тоже самое справедливо для обработчиков событий. Код, который "навесил" обработчик на конкретный элемент, знает максимум деталей об элементе и его предназначении. Например, обработчик на определённом `<td>` скорее всего подходит только для этого конкретного `<td>`, он знает все о нём, поэтому он должен отработать первым. Далее имеет смысл передать обработку события родителю -- он тоже понимает, что происходит, но уже менее детально, далее – выше, и так далее, до самого объекта `document`, обработчик на котором реализовывает самую общую функциональность уровня документа.
224
222
225
-
Всплытие и погружение являются основой для "делегирования событий" -- очень мощного приёма обработки событий. Его мы изучим в следующей главе.
223
+
Всплытие и погружение являются основой для "делегирования событий" -- очень мощного приёма обработки событий. Его мы изучим в следующей главе.
Copy file name to clipboardExpand all lines: 2-ui/2-events/03-event-delegation/article.md
+16-16Lines changed: 16 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,9 +30,9 @@
30
30
</table>
31
31
```
32
32
33
-
В этой таблице всего 9 ячеек, но могло быть и 99, и даже 9999, не важно.
33
+
В этой таблице всего 9 ячеек, но могло бы быть и 99, и даже 9999, не важно.
34
34
35
-
**Наша задача -- реализовать подсветку ячейки `<td>` при клике.**
35
+
**Наша задача - реализовать подсветку ячейки `<td>` при клике.**
36
36
37
37
Вместо того, чтобы назначать обработчик `onclick` для каждой ячейки `<td>` (их может быть очень много) -- мы повесим "единый" обработчик на элемент `<table>`.
3. Если таблицы вложенные, `event.target` может содержать элемент `<td>`, находящийся вне текущей таблицы. В таких случаях мы должны проверить действительно ли это `<td>`*нашей таблицы*.
106
106
4. И если это так, то подсвечиваем его.
107
107
108
-
## Применение делегирования: действия в разметке
108
+
В итоге мы получили короткий код подсветки, быстрый и эффективный, которому совершенно не важно, сколько всего в таблице `<td>`.
109
109
110
-
Делегирование событий может быть использовано для оптимизации обработчиков. Мы используем один обработчик для схожих действий на многих элементах. Как мы делали это с подсветкой `<td>`.
110
+
## Применение делегирования: действия в разметке
111
111
112
-
Но мы также можем применять делегирование, используя единый обработчик, во многих других ситуациях.
112
+
Есть и другие применения делегирования.
113
113
114
-
Например, нам нужно сделать меню с разными кнопками: "Сохранить", "Загрузить", "Поиск" и т.д. И есть объект с соответствующими методами `save`, `load`, `search`....
114
+
Например, нам нужно сделать меню с разными кнопками: "Сохранить (save)", "Загрузить (load)", "Поиск (search)" и т.д. И есть объект с соответствующими методами `save`, `load`, `search`... Как их состыковать?
115
115
116
116
Первое, что может прийти в голову – это найти каждую кнопку и назначить ей свой обработчик среди методов объекта. Но существует более элегантное решение. Мы можем добавить один обработчик для всего меню и атрибуты `data-action` для каждой кнопки в соответствии с методами, которые они вызывают:
Обратите внимание, что метод `this.onClick` в строке отмеченной звёздочкой `(*)`, привязывается к контексту текущего объекта `this`. Это важно, т.к. иначе `this` внутри него будет ссылаться на DOM-элемент (`elem`), а не на объект меню, и `this[action]` будет не тем, что нам нужно.
164
+
Обратите внимание, что метод `this.onClick` в строке, отмеченной звёздочкой `(*)`, привязывается к контексту текущего объекта `this`. Это важно, т.к. иначе `this` внутри него будет ссылаться на DOM-элемент (`elem`), а не на объект `Menu`, и `this[action]` будет не тем, что нам нужно.
Делегирование событий можно использовать для добавления элементам "поведения" (behavior), *декларативно* задавая хитрые обработчики установкой специальных HTML-атрибутов и классов.
178
178
179
179
Приём проектирования "поведение" состоит из двух частей:
180
-
1. Элементу ставится атрибут, описывающий его поведение.
181
-
2. При помощи делегирования ставится обработчик на документ, который ловит все клики (или другие события) и, если элемент имеет нужный атрибут -- производит нужное действие.
180
+
1. Элементу ставится пользовательский атрибут, описывающий его поведение.
181
+
2. При помощи делегирования ставится обработчик на документ, который ловит все клики (или другие события) и, если элемент имеет нужный атрибут -- производит соответствующее действие.
182
182
183
-
### Пример "Счётчик"
183
+
### Поведение: "Счётчик"
184
184
185
185
Например, здесь HTML-атрибут `data-counter` добавляет кнопкам поведение: "увеличить значение при клике":
@@ -204,14 +204,14 @@ One more counter: <input type="button" value="2" data-counter>
204
204
Элементов с атрибутом `data-counter` может быть сколько угодно. Новые могут добавляться в HTML-код в любой момент. При помощи делегирования мы, фактически, добавили новый "псевдостандартный" атрибут в HTML, который добавляет элементу новую возможность ("поведение").
205
205
206
206
```warn header="Всегда используйте метод `addEventListener` для обработчиков на уровне документа"
207
-
Когда мы устанавливаем обработчик событий на объект `document`, мы всегда должны использоваать метод `addEventListener`, а не `document.onclick`, т.к. в случае последнего могут возникать конфликты: новые обработчики будут перезаписывать уже существующие.
207
+
Когда мы устанавливаем обработчик событий на объект `document`, мы всегда должны использоваать метод `addEventListener`, а не `document.on<событие>`, т.к. в случае последнего могут возникать конфликты: новые обработчики будут перезаписывать уже существующие.
208
208
209
209
Для реального проекта совершенно нормально иметь много обработчиков на элементе `document`, установленных из разных частей кода.
210
210
```
211
211
212
-
### Пример "Переключатель" (Toggler)
212
+
### Поведение: "Переключатель" (Toggler)
213
213
214
-
Ещё один пример. Сделаем так, что при клике на элемент с атрибутом `data-toggle-id` будет скрываться/показываться элемент с заданным `id`:
214
+
Ещё один пример поведения. Сделаем так, что при клике на элемент с атрибутом `data-toggle-id` будет скрываться/показываться элемент с заданным `id`:
215
215
216
216
```html autorun run height=60
217
217
<button *!*data-toggle-id="subscribe-mail"*/!*>
@@ -236,7 +236,7 @@ One more counter: <input type="button" value="2" data-counter>
236
236
</script>
237
237
```
238
238
239
-
Ещё раз заметим, что мы сделали. Теперь для того, чтобы добавить скрытие-раскрытие любому элементу, даже не надо знать JavaScript, можно просто написать атрибут `data-toggle-id`.
239
+
Ещё раз подчеркнём, что мы сделали. Теперь для того, чтобы добавить скрытие-раскрытие любому элементу, даже не надо знать JavaScript, можно просто написать атрибут `data-toggle-id`.
240
240
241
241
Это бывает очень удобно -- не нужно писать JavaScript-код для каждого элемента, который должен так себя вести. Просто используем поведение. Обработчики на уровне документа сделают это возможным для элемента в любом месте страницы.
0 commit comments