使用e.stopPropagation()来防止事件冒泡的利弊

很多人都解释说e.stopPropagation()可以防止事件冒泡。 但是,我很难找到为什么人们想要或者想要首先防止事件冒泡。

在我的网站上,我有许多这样的元素,如下所示:

 $(document.body).on('click', ".clickable", function(e){ //e.stopPropagation(); //do something, for example show a pop-up or click a link });  

outside stuff

我想添加e.stopPropagation()因为我想使用这个真棒触摸库Hammer.js将事件处理程序从'click'更改为'touch' 。 。 这将允许点击在桌面上正常发生以及在移动设备上进行触摸事件。

这个问题(请纠正我,如果我错了)是滚动触摸设备减慢停止。

这是e.stopPropgation()有用的地方吗? 这样,无论何时触摸屏幕document.body每次都不会发生事件 – 事件冒泡?

有几种方法可以处理javascript / jQuery中的事件。 其中两个是:

  1. 您可以在对象上使用直接事件处理程序。
  2. 您可以使用委派事件处理来处理父项上的传播事件。

如果您在对象上使用直接事件处理程序并且页面中没有配置委派事件处理程序,则没有理由使用e.stopPropagation()

但是,如果您已委派使用传播的事件处理程序,则有时需要确保更高级别的委托事件处理程序不会触发当前事件。

在您的特定示例中:

 $(document.body).on('click', "a.ajaxLink", function(e){ 

这是一个委托的事件处理程序。 它正在查找传播到document.body对象的任何单击事件,该事件源自a.ajaxLink对象。 在这里, e.stopPropagation()几乎没有什么优势,因为事件几乎已经完全传播了(它也会上传到document ,但除非你还有一个处理器来click document对象,那么没有理由去e.stopPropagation()此处理程序中的e.stopPropagation()

如果您同时拥有一个顶级委托事件处理程序(如示例中的那个)并且您具有较低级别的事件处理程序(直接在对象上或使用委托事件处理,但在在document.body对象下面的级别。在这种情况下,如果你只想让低级事件处理程序获取事件,那么你可以在它的处理程序中调用e.stopPropagation() ,这样document.body处理程序就不会看到事件了。

 $("a.ajaxLink").click(function(e) { if (some condition) { // do something specific to this condition code here // stop propagation so the default behavior for click in document.body does not fire e.stopPropagation(); } }) 

注意:使用jQuery事件处理程序中的return false会触发e.stopPropagation()e.preventDefault() 。 但是,如果您在委托事件处理程序中,则e.preventDefault()不会执行任何操作,因为当目标对象第一次看到事件时,已经触发了默认行为(如果有的话)。 默认行为发生在事件传播之前,因此e.preventDefault()仅适用于直接在目标对象上的事件处理程序。


没有明显的性能下降,因为你允许事件冒泡,因为这些是用户级事件,并且它们的发生速度不够快,而且当所有中间对象上没有处理程序时,冒泡特别慢。 系统已经特殊情况下像mousemove这样的事件可以迅速解决这个问题。 如果您有一个包含数百或数千个事件处理程序的巨型项目,则有时使用委派事件处理更有效,并且有些情况下实际目标对象上的直接事件处理程序更有效。 但是,除了在巨大的场景中,性能差异可能并不明显。

这是一个鼓泡/委托更有效的例子。 你有一个包含数千行的巨型表,每行有两个按钮(添加/删除说)。 通过委派事件处理,您可以处理附加到表对象的两个简单事件处理程序中的所有按钮(按钮的公共父级)。 这将更快地安装事件处理程序,而不是直接在每个按钮上安装数千个事件处理程序。 这些委派的事件处理程序也将自动处理表中新创建的行/对象。 这是事件冒泡/委托事件处理程序的理想方案。 请注意,在这种情况下,没有理由停止传播/冒泡。

以下是委托事件处理程序效率非常低的示例。 假设您有一个包含数百个对象和事件处理程序的大型网页。 您可以使每个事件处理程序都是附加到document对象的委托事件处理程序。 但是,这就是发生的事情。 点击发生。 实际对象上没有事件处理程序,因此它会冒泡。 最终,它到达文档对象。 文档对象有数百个事件处理程序。 事件处理引擎(在本例中为jQuery)必须查看每个事件处理程序,并将委托事件处理程序中的选择器与原始事件目标进行比较,以查看它们是否匹配。 其中一些比较并不快,因为它们可以是完整的CSS选择器。 它必须为数百个委派事件做到这一点。 这对性能不利。 这正是为什么jQuery中的.live()被弃用的原因,因为它以这种方式工作。 相反,委托事件处理程序应尽可能靠近目标对象放置(根据具体情况,最接近的父对象)。 并且,当不需要委托事件处理程序时,处理程序应该放在实际目标对象上,因为这在运行时是最有效的。


回到原来的问题。 没有时间你想要一般关闭冒泡。 正如我在前面的回答中所描述的,有一些特定的实例,树上的事件处理程序想要处理事件并阻止DOM树中更高级别的任何委托事件处理程序处理此事件。 这是e.stopPropatation()的时间。


以下是其他几个相关post,其中包含有关此主题的有用信息(之前已广泛讨论过):

为什么不把Javascript事件委托带到极致?

是应该将所有jquery事件绑定到$(文档)?

jQuery.on()是否适用于在创建事件处理程序后添加的元素?

jQuery on()和stopPropagation()

避免与将大量DOM对象绑定到click事件相关的内存或性能问题的最佳实践

jQuery .live()vs .on()方法,用于在加载动态html后添加click事件

想象一下,你有一个按钮,想要处理一个点击事件:

  

如果没有事件传播,单击图像或文本将不会触发绑定到按钮的单击事件处理程序,因为事件永远不会离开元素。


您不希望事件传播的情况是嵌套元素具有自己的事件处理程序。 这是一个例子:

  

如果不使用.arrow的事件处理程序停止事件传播,它也会触发按钮的事件处理程序。

如果可能,请不要使用stopPropagation()

使用stopPropagation()两个优点是:

  • 编写代码(独立事件函数)更容易
  • 性能

虽然这个函数似乎很有用,但它可以被认为是错误的编码风格,尤其是当您无法完全控制代码时(例如,因为您使用的是第三方库)。 stopPropagation()是一个全有或全无的概念。 它不允许精细控制流程。 如果两个其他嵌套元素之间的某个元素停止事件传播,则没有任何父元素会接收它,尽管可能存在情况,它们何时应该接收它。

解决这个问题的一种更优雅(而不是那么复杂)的方法是始终允许事件传播,从不调用stopPropagation() 。 定义自己不会从子元素自动执行的事件的元素可以使用target和currentTarget属性来检查初始事件的来源,并仅在需要时执行自己的事件函数。

在下面的示例中,有3个嵌套的DIV。 单击最低(蓝色)DIV应将onClick事件向上传播到整个DOM结构,但不调用绿色DIV的onClick事件,该事件位于:

         

因此,如果DIV#2阻止触发onClick事件,则无需在DIV#3的onClick事件函数中调用stopPropagation()

另请注意,文档结构如下:

 document document.documentElement document.body ... 

如果未停止事件传播,它将到达document对象。 event.currentTarget将是document ,而event.target将是document.documentElementdocument.body元素下的任何子元素。

所以考虑到你有以下代码:

        
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world

以下是它的外观以及文档的不同部分: 简单的页面

灰色是身体区域。 绿色是实际文档“元素”(最顶部的样式部分)。 在它背后,有一个看不见的document对象。

如果要使用事件函数( 仅对手指/鼠标光标下的元素执行),可以使用以下代码(onClick事件的示例):

 elm.addEventListener('click', function(e) { if ( ( (e.currentTarget == document) && (e.target == document.documentElement || e.target == document.body) ) || (e.currentTarget == e.target) ) { // ... } }); 

它适用于document.documentElementdocument.bodydocument.body任何元素,无需调用stopPropagation()