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
+33-16Lines changed: 33 additions & 16 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -75,7 +75,7 @@ Check it out:
75
75
76
76
[codetabs height=220 src="bubble-target"]
77
77
78
-
It's possible that `event.target`equals `this` -- when the click is made directly on the `<form>` element.
78
+
It's possible that `event.target`could equal `this` -- it happens when the click is made directly on the `<form>` element.
79
79
80
80
## Stopping bubbling
81
81
@@ -110,7 +110,7 @@ For instance:
110
110
111
111
1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that the outer menu won't trigger.
112
112
2. Later we decide to catch clicks on the whole window, to track users' behavior (where people click). Some analytic systems do that. Usually the code uses `document.addEventListener('click'…)` to catch all clicks.
113
-
3. Our analytic won't work over the area where clicks are stopped by `stopPropagation`. We've got a "dead zone".
113
+
3. Our analytic won't work over the area where clicks are stopped by `stopPropagation`. Sadly, we've got a "dead zone".
114
114
115
115
There's usually no real need to prevent the bubbling. A task that seemingly requires that may be solved by other means. One of them is to use custom events, we'll cover them later. Also we can write our data into the `event` object in one handler and read it in another one, so we can pass to handlers on parents information about the processing below.
116
116
```
@@ -120,26 +120,27 @@ There's usually no real need to prevent the bubbling. A task that seemingly requ
120
120
121
121
There's another phase of event processing called "capturing". It is rarely used in real code, but sometimes can be useful.
122
122
123
-
The standard [DOM Events](http://www.w3.org/TR/DOM-Level-3-Events/) describes 3 phases of event propagation:
123
+
The standard [DOM Events](https://www.w3.org/TR/DOM-Level-3-Events/) describes 3 phases of event propagation:
124
124
125
125
1. Capturing phase -- the event goes down to the element.
126
126
2. Target phase -- the event reached the target element.
127
127
3. Bubbling phase -- the event bubbles up from the element.
128
128
129
-
Here's the pictureof a click on `<td>` inside a table, taken from the specification:
129
+
Here's the picture, taken from the specification, of the capturing `(1)`, target `(2)` and bubbling `(3)` phases for a click event on a `<td>` inside a table:
130
130
131
131

132
132
133
-
That is: for a click on `<td>` the event first goes through the ancestors chain down to the element (capturing), then it reaches the target, and then it goes up (bubbles), calling handlers on its way.
133
+
That is: for a click on `<td>` the event first goes through the ancestors chain down to the element (capturing phase), then it reaches the target and triggers there (target phase), and then it goes up (bubbling phase), calling handlers on its way.
134
134
135
-
**Before we only talked about bubbling, because the capturing phase is rarely used. Normally it is invisible to us.**
135
+
Until now, we only talked about bubbling, because the capturing phase is rarely used.
136
136
137
-
Handlers added using `on<event>`-property or using HTML attributes or using `addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases.
137
+
In fact, the capturing phase was invisible for us, because handlers added using `on<event>`-property or using HTML attributes or using two-argument`addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases.
138
138
139
139
To catch an event on the capturing phase, we need to set the handler `capture` option to `true`:
140
140
141
141
```js
142
142
elem.addEventListener(..., {capture:true})
143
+
143
144
// or, just "true" is an alias to {capture: true}
144
145
elem.addEventListener(..., true)
145
146
```
@@ -149,11 +150,10 @@ There are two possible values of the `capture` option:
149
150
- If it's `false` (default), then the handler is set on the bubbling phase.
150
151
- If it's `true`, then the handler is set on the capturing phase.
151
152
152
-
Note that while formally there are 3 phases, the 2nd phase ("target phase": the event reached the element) is not handled separately: handlers on both capturing and bubbling phases trigger at that phase.
153
153
154
-
If one puts capturing and bubbling handlers on the target element, the capture handler triggers last in the capturing phase and the bubble handler triggers first in the bubbling phase.
154
+
Note that while formally there are 3 phases, the 2nd phase ("target phase": the event reached the element) is not handled separately: handlers on both capturing and bubbling phases trigger at that phase.
155
155
156
-
Let's see it in action:
156
+
Let's see both capturing and bubbling in action:
157
157
158
158
```html run autorun height=140 edit
159
159
<style>
@@ -181,19 +181,36 @@ The code sets click handlers on *every* element in the document to see which one
181
181
182
182
If you click on `<p>`, then the sequence is:
183
183
184
-
1.`HTML` -> `BODY` -> `FORM` -> `DIV` (capturing phase, the first listener):
185
-
2.`P` (target phase, triggers two times, as we've set two listeners: capturing and bubbling)
186
-
3.`DIV` -> `FORM` -> `BODY` -> `HTML` (bubbling phase, the second listener).
184
+
1.`HTML` -> `BODY` -> `FORM` -> `DIV -> P` (capturing phase, the first listener):
185
+
2.`P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (bubbling phase, the second listener).
186
+
187
+
Please note, the `P` shows up twice, because we've set two listeners: capturing and bubbling. The target triggers at the end of the first and at the beginning of the second phase.
187
188
188
189
There's a property `event.eventPhase` that tells us the number of the phase on which the event was caught. But it's rarely used, because we usually know it in the handler.
189
190
190
191
```smart header="To remove the handler, `removeEventListener` needs the same phase"
191
192
If we `addEventListener(..., true)`, then we should mention the same phase in `removeEventListener(..., true)` to correctly remove the handler.
192
193
```
193
194
195
+
````smart header="Listeners on the same element and same phase run in their set order"
196
+
If we have multiple event handlers on the same phase, assigned to the same element with `addEventListener`, they run in the same order as they are created:
197
+
198
+
```js
199
+
elem.addEventListener("click", e => alert(1)); // guaranteed to trigger first
200
+
elem.addEventListener("click", e => alert(2));
201
+
```
202
+
````
203
+
204
+
```smart header="The `event.stopPropagation()` during the capturing also prevents the bubbling"
205
+
The `event.stopPropagation()` method and its sibling `event.stopImmediatePropagation()` can also be called on the capturing phase. Then not only the futher capturing is stopped, but the bubbling as well.
206
+
207
+
In other words, normally the event goes first down ("capturing") and then up ("bubbling"). But if `event.stopPropagation()` is called during the capturing phase, then the event travel stops, no bubbling will occur.
208
+
```
209
+
210
+
194
211
## Summary
195
212
196
-
The event handling process:
213
+
When an event happens -- the most nested element where it happens gets labeled as the "target element" (`event.target`).
197
214
198
215
- Then the event moves down from the document root to `event.target`, calling handlers assigned with `addEventListener(..., true)` on the way (`true` is a shorthand for `{capture: true}`).
199
216
- Then handlers are called on the target element itself.
@@ -203,11 +220,11 @@ Each handler can access `event` object properties:
203
220
204
221
- `event.target` -- the deepest element that originated the event.
205
222
- `event.currentTarget` (=`this`) -- the current element that handles the event (the one that has the handler on it)
206
-
- `event.eventPhase` -- the current phase (capturing=1, bubbling=3).
223
+
- `event.eventPhase` -- the current phase (capturing=1, target=2, bubbling=3).
207
224
208
225
Any event handler can stop the event by calling `event.stopPropagation()`, but that's not recommended, because we can't really be sure we won't need it above, maybe for completely different things.
209
226
210
-
The capturing phase is used very rarely, usually we handle events on bubbling. And there's a logic behind that.
227
+
The capturing phase is used very rarely, usually we handle events on bubbling. And there's a logical explanation for that.
211
228
212
229
In real world, when an accident happens, local authorities react first. They know best the area where it happened. Then higher-level authorities if needed.
0 commit comments