简体   繁体   English

如何将事件冒泡到文档而不是文档。

[英]How can an event bubble to document but not to document.body?

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 我已经创建了一个代码笔来说明这一点: 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. 一个在文档上,一个在document.body上。

When clicking on the button, only the console.log from the document would show up. 单击按钮时,只显示文档中的console.log。

Why? 为什么? Wouldn't the event bubble to document.body first and then to document? 这个事件不会冒泡到document.body首先然后再记录?

Or asked differently: how can an event bubble up to document but not stop by document.body? 或者提出不同的问题:如何将事件冒泡到文档而不是由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.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <button>Click me</button> 

This has got be a jQuery event related problem. 这已成为jQuery事件相关的问题。 With the normal DOM API both document and document.body get notified correctly: 使用普通的DOM API, documentdocument.body都会得到正确的通知:

 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 . TL / DR :事件对bodydocument起泡。 The button is not in DOM at that moment. 那时按钮不在DOM中。 But for body , before handler execution jQuery tries to ensure that button exists inside it. 但是对于body ,在处理程序执行之前,jQuery会尝试确保其中存在button For document , id does't do such a check. 对于document ,id不做这样的检查。

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. 首先,处理程序分别绑定到documentbody ,而不是按钮。 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. 当事件冒泡到document ,jQuery会尝试查找由selector(在我们的例子中为button )指定的后代,并针对每个子句执行处理程序。 Same for body . 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 . 当事件冒泡到documentbody ,该按钮已从DOM中删除,但按钮元素仍然存在于内存中,可通过event.target访问。

for both document and body jQuery searches the elements to execute the handler against: 对于documentbody jQuery搜索元素以执行处理程序:

  1. adds event.target ( our removed button ) 添加event.target我们删除的按钮
  2. adds all buttons found inside parent ( body or document ) ( founds none ) 增加了母体(内发现的所有按钮bodydocument )( 开创无
  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. 第3步的差异。

2 pieces of jQuery code are responsible for this: 2个jQuery代码负责这个:

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

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

https://github.com/jquery/jquery/blob/e743cbd28553267f955f71ea7248377915613fd9/external/sizzle/dist/sizzle.js#L1925 https://github.com/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. 第一件设置outermostContext变量。

For document , the outermostContext is true , for body it's body . 对于documentoutermostContexttrue ,对于body来说是body

Second piece sets variable ret which decides will the handler be called or not. 第二部分设置变量ret ,它决定是否调用处理程序。

For body , context !== outermostContext is false so matcher proceeds to 'matchContext' and eventually returns false . 对于bodycontext !== outermostContextfalse因此matcher继续执行'matchContext'并最终返回false

For document , context !== outermostContext is true so the matcher returns true without context check. 对于documentcontext !== outermostContexttrue因此匹配器返回true而不进行上下文检查。

The meaning of this is that for body it tries to ensure that button exists inside it. 这意味着,对于body来说,它会尝试确保button存在于其中。 For document - it does not. 对于文件 - 它没有。

Its because there are binding priorities in jQuery. 因为jQuery中存在绑定优先级。 When you click on a element this is the order javascript executes the event. 当您单击元素时,这是javascript执行事件的顺序。

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. 您使用JQuery的方式是让所有侦听器“指向”按钮元素。

According to documentation of jQuery ( http://api.jquery.com/on/ ), the code: 根据jQuery( http://api.jquery.com/on/ )的文档,代码:

$(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. 如果选择器为null或省略,则事件始终在到达所选元素时触发。

You can see this if you inspect the element with the browser and goes to event listeners 如果使用浏览器检查元素并转到事件侦听器,则可以看到此信息

在此输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM