最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How can an event bubble to document but not to document.body? - Stack Overflow

programmeradmin1浏览0评论

Recently I ran into some interesting behaviour related to event-bubbling. I have created a code-pen to illustrate this:

All I am doing there is bind two event-listeners (or I guess three including the one that removes the button). One on the document and one on the document.body.

When clicking on the button, only the console.log from the document would show up.

Why? Wouldn't the event bubble to document.body first and then to document?

Or asked differently: how can an event bubble up to document but not stop by document.body?

$(document).on("click", "button", () => console.log("document knows"));

$(document.body).on("click", "button", () =>
  console.log("document body knows")
);

$("button").on("click", e => $("button").remove());
<script src=".3.1/jquery.min.js"></script>
<button>Click me</button>

Recently I ran into some interesting behaviour related to event-bubbling. I have created a code-pen to illustrate this: https://codepen.io/anon/pen/XGmxXr

All I am doing there is bind two event-listeners (or I guess three including the one that removes the button). One on the document and one on the document.body.

When clicking on the button, only the console.log from the document would show up.

Why? Wouldn't the event bubble to document.body first and then to document?

Or asked differently: how can an event bubble up to document but not stop by document.body?

$(document).on("click", "button", () => console.log("document knows"));

$(document.body).on("click", "button", () =>
  console.log("document body knows")
);

$("button").on("click", e => $("button").remove());
<script src="https://cdnjs.cloudflare./ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button>Click me</button>

Share Improve this question edited Feb 28, 2019 at 16:25 jantimon 38.2k23 gold badges126 silver badges193 bronze badges asked Feb 28, 2019 at 16:09 G. EgliG. Egli 2131 silver badge7 bronze badges 2
  • If you change the button click to be a log than it is fine..... something weird going on with the remove() – epascarello Commented Feb 28, 2019 at 16:14
  • 1 This is related to how JQuery binds these events and checks if they should still trigger the event. I have no idea exactly what the issue with it is without diving into the jquery source code again. But if you write functionally identical code in vanilla JS ( using something like event.target.nodeName === 'BUTTON' inside the doc and body handler ), both the document and the body log their knowledge, as you would expect. – Shilly Commented Feb 28, 2019 at 16:25
Add a ment  | 

4 Answers 4

Reset to default 5

This has got be a jQuery event related problem. With the normal DOM API both document and document.body get notified correctly:

document.addEventListener("click", (e) => {
    if (e.target.tagName === 'BUTTON') {
      console.log("document knows");
    };
});

document.body.addEventListener("click", (e) => {
    if (e.target.tagName === 'BUTTON') {
      console.log("document.body knows");
    };
});

document.querySelector("button").addEventListener("click", function(e) {
  this.parentElement.removeChild(this);
})
<button type="button">Click me</button>

TL/DR: The event bubbles to both body and document. The button is not in DOM at that moment. But for body, before handler execution jQuery tries to ensure that button exists inside it. For document, id does't do such a check.

Explanation

I'm not sure why it's designed like this. It's just explanation why it happens.

First of all, the handlers are bound to document and body respectively, not to the button. When event bubbles to the document, jQuery tries to find descendants, specified by selector (button in our case) and executes the handler against each of them. Same for body. The difference is in the way how it checks the descendants.

When the event is bubbled to document and body, the button is already removed from DOM, but the button element still exists in memory and is accessible via event.target.

for both document and body jQuery searches the elements to execute the handler against:

  1. adds event.target (our removed button)
  2. adds all buttons found inside parent (body or document) (founds none)
  3. filters the result (which is just our button) by some matchers to check if the buttons are really children of parent
  4. Executes the handlers against found elements.

The difference in 3rd step.

2 pieces of jQuery code are responsible for this:

https://github./jquery/jquery/blob/e743cbd28553267f955f71ea7248377915613fd9/external/sizzle/dist/sizzle.js#L1985

outermostContext = context === document || context || outermost;

https://github./jquery/jquery/blob/e743cbd28553267f955f71ea7248377915613fd9/external/sizzle/dist/sizzle.js#L1925

var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
                (checkContext = context).nodeType ?
                    matchContext( elem, context, xml ) :
                    matchAnyContext( elem, context, xml ) );

First piece sets outermostContext variable.

For document, the outermostContext is true, for body it's body.

Second piece sets variable ret which decides will the handler be called or not.

For body, context !== outermostContext is false so matcher proceeds to 'matchContext' and eventually returns false.

For document, context !== outermostContext is true so the matcher returns true without context check.

The meaning of this is that for body it tries to ensure that button exists inside it. For document - it does not.

Its because there are binding priorities in jQuery. When you click on a element this is the order javascript executes the event.

1. Event on document
2. Event on element
3. Event on the elements parent
4. Event on the parents parent
5. Ect.. Until you arrive to the body.

So what is happening is that you delete the button before you actually arrive to the body.

The way that you use JQuery, you was making all listeners "point" to the button element.

According to documentation of jQuery (http://api.jquery./on/), the code:

$(document.body).on("click", "button", ...

and the code:

$(document).on("click", "button", ...

Do the same thing, they add a listener on your "button" element.

The second parameter is a selector for the element or sub elements that you want to put your listener.

selector

Type: String

A selector string to filter the descendants of the selected elements that trigger the event. If the selector is null or omitted, the event is always triggered when it reaches the selected element.

You can see this if you inspect the element with the browser and goes to event listeners

发布评论

评论列表(0)

  1. 暂无评论