1 | <div onclick="alert('The handler!')"> |
This handler is assigned to div, but also runs if you click any nested tag like em or code:
Why does the handler on div run even if the actual click was on em?
Bubbling
When an event happens on an element(like click on a button), it first runs the handlers on it, then on its parent, then all the way up on other ancestors.
event.target
A handler on a parent element can always get the information about where it actually happened.
The most deeply nested element that caused the event is called a target element, accessible as event.target.
Note the differences from
this (=event.currentTarget): this – is the “current” element, the one that has a currently running handler on it.
event.target – is the “target” element that initiated the event, it doesn’t change through the bubbling process.
example:form
For instance, if we have a single handler form.onclick, then it can “catch” all clicks inside the form. No matter where the click happened, it bubbles up to
In form.onclick handler:
this (=event.currentTarget) is the
Stop bubbling
A bubbling event goes from the target element straight up. Normally it goes upwards till <html>
, and then to document
object, and some events even reach window
, calling all handlers on the path.
**But any handler can decide that the event has been fully processed and stop the bubbling.**which is to say “this is enough, I don’t want this event to be known by other handlers”.
The method for it is event.stopPropagation()
.
1 | <body onclick="alert(`the bubbling doesn't reach here`)"> |
event.stopImmediatePropagation()
.
If an element has multiple event handlers on a single event, then even if one of them stops the bubbling, the other ones still execute.
In other words, event.stopPropagation()
stops the move upwards, but on the current element all other handlers will run.
To stop the bubbling and prevent handlers on the current element from running, there’s a method event.stopImmediatePropagation()
. After you use this event.stopImmediatePropagation()
no other handlers will execute.
event.preventDefault();
What is default behavior?
It is the default behavior of the browser in response to the event like click, keypress, submit, etc.
For example:
- Click the “Submit” button in the form, the browser will submit the form by default.
- Press the Enter key in the text box, the browser will perform the submit operation by default.
- Trigger the
copy
event, the browser will copy the selected text to the clipboard by default.
Capturing
There’s another phase of event processing called “capturing”. It is rarely used in real code, but sometimes can be useful.
The standard DOM Events describes 3 phases of event propagation:
- Capturing phase – the event goes down to the element.
- Target phase – the event reached the target element.
- Bubbling phase – the event bubbles up from the element.
addeventListener
Its third parameter is a Boolean value. By setting true/false, the listener is designed in different stages. True means in the capture stage, and false means in the bubbling stage.
1 | <body> |
1 | let button = document.querySelector("button"); |
当你点击 span 时,会依次输出 span button body,因为事件是从 span 开始冒泡到 button 再冒泡到 body 的。
但当你点击 body 只有 body 会输出 因为已经到达了元素 他往上冒泡没有监听器了。
1 | let button = document.querySelector("button"); |
现在当你点击 body 会依次输出 body 也只会输出 body 因为已经到达元素 target 了 当你点击 body body 就是目标元素 这个捕获阶段是从 window->body 而不是 body->button->span
事件流
There are both bubbling and capturing, but according to the previous roller coaster, capturing takes precedence over bubbling. When you trigger an event, the browser first checks if there is a listener in your capture phase, then the listener on your element, and then checks if there is a listener in your bubbling phase.
点击元素,触发一个事件流到达他 浏览器从 window 开始捕获到达元素 再从元素冒泡到 window 沿路发现 mother 和 daughter 的监听器 然后到达元素 baby 的 冒泡到达 grandma 不会再去执行 mother 和 daughter 的监听器 因为这个监听器设在捕获阶段