Problem a.

If an element and its ancestor elements register the same type of event functions (such as clicks, etc.), what is the order in which the event functions are called when the event occurs?

For example, consider the following nested elements:

----------------------------------- | outer | | ------------------------- | | |inner | | | ------------------------- | |  | -----------------------------------Copy the code

Both elements have onclick handlers. If the user clicks on the inner, the event handlers on both the inner and outer are called. But who comes first?

Two. Two models

In the bad old days, Netscape and M$saw things differently.

Netscape believes that handlers on outer should be executed first. This is called event capturing.

M$assumes that the handler on inner has execution priority. It’s called Event bubbling.

Event capturing

               | |
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner     \ /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------
Copy the code

The event handler on the outer fires first, then on the inner.

Event Bubbling

               / \
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner     | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------
Copy the code

In contrast to event capture, when event bubbling is used, the event handler on the inner is fired first, followed by the one above the outer.

Iii. W3C model

The W3C standard takes a compromise. Any event that occurs in the W3C event model is first captured (from its ancestor element document) all the way down until it reaches the target element, and then bubbles from the target element again.

1. The first capture down on | | | / \ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | -- - | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | the outer | | | | | | -- -- -- -- -- -- -- -- -- -- -- - | | -- - | | -- -- -- -- -- -- -- -- -- -- - | |  | inner \ / | | | | | | | | | | | 2. Arriving at the target element from down to up the bubble | | | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - | | the W3C event model | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Copy the code

You, as the developer, can decide whether the event handler is registered in the capture or bubble phase. If the last argument of addEventListener is true, then the handler will be fired during the capture phase; Otherwise (false), it will be triggered during the bubble phase.

For example:

var selector = document.querySelector.bind(document);
selector('div.outer').addEventListener('click', (e) => {
    selector('p:first-of-type').textContent += 'outer clicked! '
}, true)
selector('div.inner').addEventListener('click', (e) => {
    selector('p:first-of-type').textContent += 'inner clicked! '
}, false)
document.addEventListener('click', (e) => {
    selector('p:first-of-type').textContent += 'document clicked! '
}, true)
Copy the code

When you click on the inner element, the following happens:

  1. Click events begin in the capture phase. At this stage, the browser looks for click event handlers on all ancestor elements of the inner (starting with document).

  2. Two event handlers are found, one on document and the other on outer, and the useCapture option for these two events is true, indicating that they are registered in the capture phase. So the click handlers for document and outer are executed.

  3. Keep looking down until you reach the inner element itself. The capture phase is over. The bubbling phase is entered and the event handler on the inner is executed.

  4. The event hits the target element and starts bubbling up, looking for event handlers on the ancestor element that registered the bubbling phase. As nothing was found, nothing happened.

The final result:

// log
document clicked! outer clicked! inner clicked!
Copy the code

If we register the ancestor element’s event handler in the bubble phase (addEventListener’s useCapture option is false):

var selector = document.querySelector.bind(document);
selector('div.outer').addEventListener('click', (e) => {
    selector('p:first-of-type').textContent += 'outer clicked! '
    console.log(e);
}, false)
selector('div.inner').addEventListener('click', (e) => {
    selector('p:first-of-type').textContent += 'inner clicked! '
    console.log(e);
}, false)
document.addEventListener('click', (e) => {
    selector('p:first-of-type').textContent += 'document clicked! '
}, false)
Copy the code

The result:

// log
inner clicked! outer clicked! document clicked!
Copy the code

The traditional model Element.onclick = function(){} will be registered in the bubble phase.

Application of event bubble

For example: the default function when clicked.

If you register a click function on document:

document.addEventlistener('click', (e) => {}, false)

Then any click event on an element will eventually bubble to the event handler and fire the function – unless the previous event handler stopped the bubble (e.topPropagation (), in which case the event will not bubble upwards).

Note: e.topPropagation () can only prevent events from propagating upward during the bubbling phase. If the ancestor element of the clicked element has an event handler registered in the capture phase:

ancestorElem.addEventListner('click', (e) => {
// do something...
}, true)
Copy the code

The event handler on the ancestor element will also be fired during the capture phase.

So, you can set a handler on the document that will fire when any element on the page is clicked. A useful example is the drop-down menu: the drop-down menu is hidden when you click anywhere above the document except the drop-down menu itself.

During the bubble or capture phase, E. Currenttarget refers to the element to which the current event handler is attached. You can also use this in the event handler instead.

Event delegate:

With the feature of event bubble, the inner event is delegated to the outer event, and the event is delegated according to the properties of the Event object to improve the performance. Using event delegates avoids adding event listeners to specific nodes; Event listeners are added to their parent elements, and the event listeners analyze the events bubbling up from the child element to find out which child element is the event.

For example: When you mouse over Li, the background of Li turns gray.

<ul>
    <li>item1</li>
    <li>item2</li>
    <li>item3</li>
    <li>item4</li>
    <li>item5</li>
    <li>item6</li>
</ul>
Copy the code

Use event bubbling to achieve:

$("ul").on("mouseover".function(e){
    $(e.target).css("background-color"."#ddd").siblings().css("background-color"."white");
})
Copy the code

It is also possible to tie events to all Li, such as:

$("li").on("mouseover".function(){
    $(this).css("background-color"."#ddd").siblings().css("background-color"."white");
})
Copy the code

In terms of code brevity, the two are similar. However, the former has one less operation to traverse all the Li nodes, so the performance is definitely better.

Also, if the page dynamically loads some elements after the binding event:

$("<li>item7</li>").appendTo("ul");
Copy the code

In the second scenario, item7 does not exist at the time the event is bound, so it has to be bound again. The bubbling solution does not need to be bound again because the event is bound to ul.

V. Trouble with M$model

In the M$model, there is no support for E. Corettarget, and even worse, this does not point to the current HTML element.

(This article is only for personal memo record purposes, some quotes from the Internet with personal understanding. Welcome to reprint, please indicate the source. Feel free to tip if it helps you.