2

Is there any way to prevent some of the listeners in an event's chain from firing for an event but allow others farther up the chain to fire?

For instance i have this structure

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

Lets say i have click event listeners attached to body, div#1, and div#2. Would it be possible at div#2 event listener to prevent the event making it to div#1 or any other listeners in between and allow the event to fire on the body element?

I say this because i am using google maps and emberjs to build a bunch of interactive infoboxes that can be shown on a map. The problem is that ember attaches event listeners on the body element and google attaches them at some other unknown level. When a click event is fired by emberjs HTML nodes hosted inside a google maps overlay the event must first pass through google maps handlers before it reaches ember's.

This is causing some unintended side effects like google maps thinking i'm clicking on other markers. IE. this issue. Mousing over Infobox fires hover event on markers that are behind it

Chris Rice
  • 728
  • 2
  • 9
  • 32
  • [Stop propagnation](https://api.jquery.com/event.stoppropagation/) – Andrew Paramoshkin Sep 21 '15 at 18:43
  • No, not really. This just isn't how even propagation works. You either stop the propagation at some point in the hierarchy or you don't. You don't skip several levels of the hierarchy and then continue event propagation. It sounds like you need to fix your problem in a more fundamental way, but we'd need a more concrete example of the actual problem with the actual tools involved to know what to suggest. – jfriend00 Sep 21 '15 at 18:45
  • @AndrewParamoshkin That is jQuery, which was not tagged on the question. And, OP specifically asked for propagation to continue, but just not trigger on some elements in the chain. From the jQuery docs: _Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event_ – Drew Gaynor Sep 21 '15 at 18:46
  • My guess is that you need to find a way to get ember to process events directly on your info boxes, not wait until things get all the way back up to the document. I don't know ember to suggest how to do that. – jfriend00 Sep 21 '15 at 18:52
  • @jfriend00, this is what i was afraid of but figured the answer would be. Thought maybe there was a way to re-trigger it at the right level after i stopped it. Changing the behavior of both ember or google seems almost out of the question as well from what i can tell. – Chris Rice Sep 21 '15 at 18:54
  • I think there are ways to solve this in Ember. See [Stop Click Propagation from Ember Action](http://stackoverflow.com/questions/22230430/stop-click-propagation-from-ember-action). And, there's a simple example of stopping a click from propagating up to the parent link tag here: http://guides.emberjs.com/v1.10.0/templates/actions/#toc_stopping-event-propagation. This seems like something ember would need to support because your situation is not unique. – jfriend00 Sep 21 '15 at 19:02

1 Answers1

1

You could conditionally execute your handler code based on what the click target was. In this example, you can see that when button A is clicked, the event fires for button b and div 1, but not div 2. If button B is clicked, the event fires for all three.

Another approach, if you are not adding the event listeners yourself but you know on which node you must trigger the event, as in the case of Ember and the body element, you could stop propagation of the event in a handler that you do control and then trigger the click event on body. This isn't the most elegant solution, but given the situation as described I'm not sure if there's another way. You can see this version by clicking button 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>
Drew Gaynor
  • 8,292
  • 5
  • 40
  • 53
  • This would work in most scenarios except for mine (sad face) because google doesn't care about what the target was when it runs its handlers only the x/y of where it occurred. I don't have any control over how it decides to muck with things. – Chris Rice Sep 21 '15 at 19:02
  • This answer appears to not take into consideration that the event handlers in the OP case are Google maps stuff and EmberJS event handlers. These are not from scratch event handlers that can be customized in this way. – jfriend00 Sep 21 '15 at 19:04
  • @ChrisRice Ah, that is unfortunate. But, I have updated my answer with another, though not exactly elegant, solution: stopping propagation and directly triggering the click as you need it. This does still require that you control at least _one_ click event handler, though, which I figured was the case given your `button` example. – Drew Gaynor Sep 21 '15 at 19:21