23

I was looking at http://www.quirksmode.org/js/events_order.html and it is ambiguous in this part:

In the Microsoft model you must set the event’s cancelBubble property to true.

window.event.cancelBubble = true

In the W3C model you must call the event’s stopPropagation() method.

e.stopPropagation()

This stops all propagation of the event in the bubbling phase.

So my question is:

  • When an event listener is set to listen in the capture phase, does it automatically not continue propagating to the inner elements?
  • Or if it does continue propagating, does calling e.stopPropagation() stop it, or does that only work for the bubble phase?
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
yic
  • 700
  • 1
  • 6
  • 13

4 Answers4

43

Short answer: The order is:

  1. Capture (down)
  2. Target
  3. Bubble (up).

If you call e.stopPropagation() in the capture phase (by setting the addEventListener()'s 3rd argument to true), it stops at 1, so 2 & 3 cannot receive it.

If you call e.stopPropagation() in the bubble phase (by setting the addEventListener()'s 3rd argument to false or just not assign it), the 1 & 2 already complete, so it just prevents the event from bubbling up from the level where you call stopPropagation().

totymedli
  • 29,531
  • 22
  • 131
  • 165
LeOn - Han Li
  • 9,388
  • 1
  • 65
  • 59
  • 1
    I think the question is more specific. Does `event.stopPropagation()` stops an event from descending though the remaining `event.target`'s ancestors? Or does it only prevents phases 2 and 3, whereas phase 1 will complete no matter what? – GetFree Mar 31 '18 at 09:06
  • 2
    This is a much better answer than the one that is currently accepted. – Luis Crespo Oct 27 '20 at 16:45
5

No, an event listener doesn't stop any events from propagating, unless you explicitly tell it to. The part you're referring to deals with the bubble phase specifically. IE's model doesn't support event capturing - full stop. the capture phase is what precedes the bubbling phase:

Top of the DOM --->event--->traverses--->to--->[target]+[event]-| (capture phase)
      /\                                                       \/
      |------------------------to--------back up-----------------  (bubble up)
Elias Van Ootegem
  • 74,482
  • 9
  • 111
  • 149
  • Thanks. It looks like IE8 doesn't support event capturing, but IE9 does: http://msdn.microsoft.com/en-us/library/windows/apps/hh453039.aspx – yic Sep 17 '12 at 17:28
  • 10
    My question is does e.stopPropagation() stop the propagation of bubbling only, or both the propagation of capturing and bubbling? Possible the answer is browser-dependent. – yic Sep 17 '12 at 17:28
  • 5
    Short answer: stopPropagation stops the event in both cases. As the (horrible) ASCII-diagram shows, the event sets of from the `document`, and descends down in the DOM, towards the element where the event was triggered. After that, it bubbles back up to the document. At every step in between this process can be stopped, regardless of it being during the capture or bubble phase. – Elias Van Ootegem Sep 17 '12 at 18:54
  • I think I understand you. Thanks for explaining so nicely. So by W3C, if I have both a capture event listener and a bubble event listener: - If only the capture event listener did e.stopPropagation(), the bubble event listener wouldn't see the event. - If the bubble event listener did e.stopPropagation(), the capture event listener would still see the event, since the event went through the capture one first. Is that right? – yic Sep 18 '12 at 03:14
  • Not entirely. The first part, you got right: stopping the event from propagating means the event wouldn't reach the bubble phase, if you stop the event while bubbling, any capturing listeners would have been fired before that: _first_ an event passes through the capturing phase, then it bubbles. If you stop while bubbling, the capturing phase has been completed... – Elias Van Ootegem Sep 18 '12 at 06:39
  • 1
    Ok, thanks :) I think we actually say the same thing... if you stop while bubbling, the capturing phase has been completed, ... therefore, stopping while bubbling should not prevent listeners which are listening in the capture phase from handling the event. – yic Sep 18 '12 at 23:16
  • @yic: No, there's an important distinction to be made: if you stop an event in the bubble phase, that has listeners for both the capture and bubble phases, the listeners in the capture phase have handled the event. They just didn't stop it. So you could set a capture handler to alert something, and a bubble listener do alert something else and call the `stopPropagation` method. This'll cause two alerts to show – Elias Van Ootegem Sep 19 '12 at 07:11
  • I agree with that, thats exactly what i was trying to express! I believe we really are saying the same thing! This is funny :) – yic Sep 20 '12 at 15:18
  • 2
    Unbelievable how this answer *still* did not actually answer the question (*does calling e.stopPropagation() stop it?*) and we need to read the comments. [Answer by Leon li](https://stackoverflow.com/a/33349894/286685) is much better. – Stijn de Witt May 01 '18 at 13:44
2

A concise answer is :
Whether it is a capturing phase or bubbling phase, DOM propagation is stopped on the first encounter of e.stopPropagation().

So in case 1 you have these elements on DOM:

el1.addEventListener('click', doSomething1, true);
el2.addEventListener('click', doSomething2, true); // Listener calls `e.stopPropagation()`
el3.addEventListener('click', doSomething3, true);

Note: On HTML page structure, el3 is a direct child of el2. And el2 is a direct child of el1.

We presume doSomething2 listener calls e.stopPropagation().

Clicking el3, listeners run in order as:

  1. CAPTURING PHASE: doSomething1 --> doSomething2 --> ❌ no any other listener is run(whether capturing or bubble phase) - so el3 listener is not run
  2. BUBBLING PHASE: ❌ Not even reachable

And in in case 2 you have these elements on DOM:


el1.addEventListener('click', doSomething1, true);
el2.addEventListener('click', doSomething2, false); // Listener calls `e.stopPropagation()`
el3.addEventListener('click', doSomething3, true);

Clicking el3, listeners run in order as:

  1. CAPTURING PHASE: doSomething1 --> doSomething3 (All capture phase listeners down the DOM tree upto the clicked el3 element have been run) - so el3 listener is run
  2. BUBBLING PHASE: doSomething2 --> ❌ Any other bubble phase listener is not run
hane Smitter
  • 516
  • 3
  • 11
0

stopPropagation() will not stop the captured event handler from getting called. stopPropagation() will stop the bubbled event handler from getting called.

Jsfiddle

var outputDiv = document.getElementById('output');

function log(msg) {
  outputDiv.insertAdjacentHTML('afterend', msg + '<br>');
}

/////////////////////
//Bubbling listeners
/////////////////////
document.getElementById('row1').addEventListener('click', function(e) {
  log('Bubbling row1 listener called');
  e.stopPropagation();
}, false);

document.getElementById('row2').addEventListener('click', function(e) {
  log('Bubbling row2 listener called');
  //NO stopPropagation on this one.
}, false);

document.getElementById('table').addEventListener('click', function() {
  log('Bubbling table listener called');
}, false);


document.addEventListener('click', function() {
  log('Bubbling document listener called');
}, false);

/////////////////////
//Capturing listeners
/////////////////////
document.addEventListener('click', function() {
  log('Capturing document listener called');
}, true);

document.getElementById('table').addEventListener('click', function() {
  log('Capturing table listener called');
}, true);
#outputwrapper {
  border: 1px solid black;
  height: 300px;
  overflow: scroll;
}
<table id="table" border="1">
  <tbody>
    <tr>
      <td id="row1">
        This row has stopPropagation
      </td>
    </tr>
    <tr>
      <td id="row2">
        This row does not have stopPropagation
      </td>
    </tr>
  </tbody>
</table>
<br>Output
<br>
<div id="outputwrapper">
  <div id="output"></div>
</div>
Ravi Gidwani
  • 198
  • 12
  • You have two problems. First, they are row 1 and row 2, not columns. Second, row 2's event handler is actually not in the capturing phase, since the third argument to its `addEventListener` is missing. The accepted answer is correct. – Chen Jul 17 '15 at 07:35
  • @Chen first off I agree with the column misnomer. I had actually shortened the code by removing some columns and had this left over code. – Ravi Gidwani Jul 31 '15 at 18:19
  • @Chen I have modified the code to reflect what I was trying to explain. – Ravi Gidwani Jul 31 '15 at 20:22