阻止某些事件侦听器触发事件​​,但允许父侦听器触发

Posted

技术标签:

【中文标题】阻止某些事件侦听器触发事件​​,但允许父侦听器触发【英文标题】:Prevent certain event listeners from firing for an event but allow the parent listeners to fire 【发布时间】:2015-12-18 13:50:43 【问题描述】:

有什么方法可以阻止事件链中的某些侦听器触发事件​​,但允许链上更远的其他侦听器触发?

例如我有这个结构

<body>
    <div id="1">
       <div id="2">
          <button>Click Me</button>
       </div>
    </div>
</body> 

假设我将单击事件侦听器附加到 body、div#1 和 div#2。是否可以在 div#2 事件侦听器中阻止事件进入 div#1 或介于两者之间的任何其他侦听器,并允许事件在 body 元素上触发?

我这样说是因为我正在使用 google maps 和 emberjs 来构建一堆可以显示在地图上的交互式信息框。问题是 ember 在 body 元素上附加了事件侦听器,而 google 将它们附加在其他未知级别。当一个 click 事件被托管在 google maps 覆盖层内的 emberjs html 节点触发时,该事件必须首先通过 google maps 处理程序,然后才能到达 ember's。

这会导致一些意想不到的副作用,例如谷歌地图认为我正在点击其他标记。 IE。这个问题。 Mousing over Infobox fires hover event on markers that are behind it

【问题讨论】:

Stop propagnation 不,不是。这甚至不是传播的工作方式。您要么在层次结构中的某个点停止传播,要么不停止传播。您不会跳过层次结构的多个级别,然后继续事件传播。听起来您需要以更基本的方式解决您的问题,但我们需要一个更具体的实际问题示例,并使用所涉及的实际工具来了解建议的内容。 @AndrewParamoshkin 那是 jQuery,没有在问题上标记。而且,OP 专门要求继续传播,但不要在链中的某些元素上触发。来自 jQuery 文档:防止事件在 DOM 树中冒泡,防止任何父处理程序收到事件通知 我的猜测是,您需要找到一种方法让 ember 直接在您的信息框上处理事件,而不是等到事情一直回到文档中。我不知道 ember 建议如何做到这一点。 @jfriend00,这是我害怕的,但我想答案会是。我想也许有办法在我停止它后在正确的水平上重新触发它。据我所知,改变 ember 或 google 的行为似乎也几乎不可能。 【参考方案1】:

您可以根据点击目标有条件地执行您的处理程序代码。在此示例中,您可以看到,当单击按钮 A 时,会为按钮 b 和 div 1 触发事件,但不会为 div 2 触发。如果单击按钮 B,则会为所有三个触发事件。

另一种方法,如果您自己没有添加事件侦听器,但您知道必须在哪个节点上触发事件,例如 Ember 和 body 元素,您可以停止在处理程序中传播事件你控制然后触发body 上的点击事件。这不是最优雅的解决方案,但鉴于所描述的情况,我不确定是否还有其他方法。您可以通过单击按钮 C 来查看此版本。

function log(s) 
  var e = document.createElement('div');
  e.innerHTML = s;
  document.body.appendChild(e);


document.getElementById('1').addEventListener('click', function (e)  log('Div 1 clicked'); );
document.getElementById('2').addEventListener('click', function (e) 
  if (event.target.id !== "a") 
      log('Div 2 clicked');
  
);
document.getElementById('a').addEventListener('click', function (e)  log('Button A clicked'); );
document.getElementById('b').addEventListener('click', function (e)  log('Button B clicked'); );

document.body.addEventListener('click', function (e)  log('Ember click event fired'); );
document.getElementById('c').addEventListener('click', function (e) 
  log('Button C clicked');
  e.stopPropagation();
  document.body.click();
);
<div id="1">
  <div id="2">
    <button id="a">Button A</button>
    <button id="b">Button B</button>
    <button id="c">Button C</button>
  </div>
</div>

【讨论】:

这在大多数情况下都可以工作,除了我的(悲伤的脸),因为当它运行它的处理程序时,谷歌并不关心目标是什么,只关心它发生位置的 x/y。我无法控制它决定如何处理事情。 这个答案似乎没有考虑到 OP 案例中的事件处理程序是谷歌地图的东西和 EmberJS 事件处理程序。这些不是可以以这种方式自定义的从头开始的事件处理程序。 @ChrisRice 啊,真不幸。但是,我已经用另一个虽然不完全优雅的解决方案更新了我的答案:停止传播并根据需要直接触发点击。不过,这仍然需要您控制至少 一个 单击事件处理程序,我认为这是给定您的 button 示例的情况。

以上是关于阻止某些事件侦听器触发事件​​,但允许父侦听器触发的主要内容,如果未能解决你的问题,请参考以下文章

无法在被动事件侦听器中阻止默认值

本地存储中的更改未触发事件侦听器

Video.js - 加载元数据事件永远不会触发

ExtJS 更改事件侦听器未能触发

前端笔记八vue指令的事件修饰符

前端笔记八vue指令的事件修饰符