Two separate event
object methods exist for stopping an event: event.stopPropagation()
and event.stopImmediatePropagation()
. This lesson will explain the differences between these two functions as well as cautioning you about some dangers in stopping events.
Alex Reardon: [0:00] It is possible for you to stop an event from continuing along the event path. You can stop an event by using event.stopPropagation() and event.stopImmediatePropagation(). My visualizer makes it really clear how the stopPropagation and stopImmediatePropagation functions work.
[0:18] Firstly, I'm going to add a few event listeners. When I dispatch the event, I see that the event flows through the event targets ordered by the event phase, so firstly, a capture phase, target phase and then bubble phase. While on an event target, event listeners are executed in the order in which they were added to that event target.
[0:40] If I come over here and I change this event listener so that it calls stopPropagation, let's see what happens. I dispatch the event. We'll see it hits this event listener and keeps going, and then it stops here. stopPropagation stops an event from continuing along an event path. It stops the event after the event listeners on the current event target in the current phase of the event have been executed.
[1:05] We'll see here that my second event listener called stopPropagation, and then these following three event listeners were called, but all of the rest of the event listeners along the event path were not called. I'll change this event listener, so it's no longer calling stopPropagation. Instead, I'm going to make this listener call stopPropagation.
[1:24] This event listener will be executed in the target phase. If I dispatch the event, I'll see it's very similar to what we saw before. These three event listeners are executed. These two event listeners were called after this event listener called stopPropagation, but this event listener over here, which is still in the same phase, so the target phase, was not executed.
[1:45] This matches the spec and also the behavior that you'll see in Safari. However, in Chrome and Firefox, at the time of recording, this event listener will be executed if this event listener over here calls event.stopPropagation.
[2:00] Given the varied behaviors across browsers, if an event listener on the target which was added in the capture phase calls event.stopPropagation, then event listeners added to the target in the bubble phase may or may not be called. If we come back to this event listener and we call stopImmediatePropagation rather than stopPropagation, let's have a look and see what happens.
[2:24] The event hits the event listener and then stops immediately. You'll see that these subsequent event listeners on the same event target in the same event phase were not called. stopImmediatePropagation is a more forceful version of stopPropagation. It will stop straightaway. No other event listeners are
[2:46] If we come back to this event listener in the target phase and I call stopImmediatePropagation on that one and dispatch the event, we'll see that these subsequent event listeners weren't called, and neither was this one. This behavior is consistent across browsers, where calling stopImmediatePropagation on an event listener on the target of an event will result in no further event listeners being called.
[3:13] Something to keep in mind is that stopping an event is not the same thing as canceling an event. They're two independent side effects on the event object. Stopping an event on the event path will not cancel the event. It will not opt out of the default behavior for that event. Vice versa, as well, if I cancel an event, then that will not stop the event from continuing along the event path.
[3:37] It's also completely allowed for an event listener to stop an event and cancel an event. Over here, we'll see that this event listener is canceling the event as well as stopping the event. In this case, we've got one event listener that's going to call stopPropagation and then another one after it that's going to call stopImmediatePropagation.
[3:55] When I dispatch the event, we'll see that this first event listener did, in fact, stop the event. As it was continuing through the subsequent event listeners on the event target, this event listener stopped the event immediately, and so the event stopped there. It didn't keep going through these event targets.
[4:12] In this setup, we have two modules adding event listeners to the window object, which is an event target. Module1 is adding a mouseup event listener in the capture phase. Module2 is adding a mousedown event listener in the bubble phase and a mouseup event listener in the bubble phase. These two modules don't know anything about each other.
[4:37] Along comes a mousedown event which is targeting a button that's currently in the capture phase, and that doesn't match any event listeners on our window object. We have a mousedown event listener over here, but it's in the bubble phase.
[4:52] This event's going to continue down towards the button. Then it's going to come back up towards the window in the bubble phase. Now, it matches this event listener over here that we have in Module2. This event listener is going to start doing some work.
[5:09] What this module is going to do is, when it receives a mouseup event, it's going to stop doing that work. The module can do this because it knows that a mouseup event comes after a mouse down event.
[5:23] Now, the event goes away. It's finished. We get another event, which is a mouseup event, which is also targeting the button that's in the capture phase, so it's going to match this event listener over here in Module1.
[5:35] This event listener is going to call event.stopPropagation, which is going to stop the event from continuing along the event path. This event won't continue down towards the button in the capture phase, and it won't come back up towards the window in the bubble phase.
[5:53] This causes a problem for Module2 because it was waiting for a mouseup event in the bubble phase to stop doing some work. However, because we've stopped the event in Module1, Module2 will never receive the mouseup event, and so the work will just continue on.
[6:09] Stopping events on the event path is a powerful feature of the DOM event system. However, you need to be careful when stopping events, as you can end up breaking other parts of your application that expect events to flow in a particular way.