There have been some attempts to answer this question:
here, here and here. However none of the answers are giving a solid response. I'm not referring to the event
phases capture
, bubble
and target
and how stopPropagation()
affects the overall event. I'm looking for a case where adding stopPropagation()
to a DOM node will benefit the overall code?

- 17,580
- 5
- 58
- 84

- 2,044
- 4
- 18
- 28
-
OK, I don't get what the question is. You benefit from `stopPropagation()` when you don't want the event to propagate further. Yes, I just told you "you need it when you need it" but I don't see a way to explain this that's not circular if you already know what propagation *is*. – VLAZ Feb 21 '19 at 20:12
-
3If you don't get the question and don't see a way to explain it, please refrain from answering. "You need it when you need it" doesn't provide any insight in coding. – KT-mongo Feb 21 '19 at 20:24
-
1"benefit the overall code" — it benefits the code when propagation to parent DOM nodes would result in undesirable behavior. It's not just something you toss in for good measure; it has a specific purpose and in a given situation you either *do* want to stop propagation or you *don't*. – Pointy Feb 21 '19 at 20:35
-
OK, what exact issue you have with understanding why you don't want event propagation to happen? You've linked to three different other questions and the information given there is the same. You don't need it when you don't need it. [If you have a situation like this](https://jsbin.com/taxagehobe/edit?console,output) where you fire multiple click handlers but you might need only some. You can stop the propagation on the button, for example. If you're not in this situation, it's not needed. – VLAZ Feb 21 '19 at 20:35
-
@Pointy, can you give me an example of undesirable behavior? Can you give me an example in a given situation when you want to stop propagation? – KT-mongo Feb 21 '19 at 20:39
-
1There is the JSBin I linked to. That's an example. Multiple event listeners for the same event on multiple elements in parent-child hierarchy. A click triggers all. – VLAZ Feb 21 '19 at 20:42
-
@VLAZ you are providing a code that simply lay's out event bubbling. How is this even remotely close to what I'm asking? Let me clarify with an example. You would use preventDefault() when submitting a form in a front-end app and you would prevent the a page refresh. – KT-mongo Feb 21 '19 at 20:46
-
"*you are providing a code that simply lay's out event bubbling*" yes, that's correct. That's what `stopPropagation` stops. That's why I don't understand what you don't understand about `stopPropagation`. "*How is this even remotely close to what I'm asking?*" Let's be even more clear - let's say you want *only* the button click handler to engage, not the others. But you still want the other click handers that do different stuff. You also can't change the HTML. Your only option is `stopPropagation`. You have to use it, so you use it. – VLAZ Feb 21 '19 at 20:51
-
"Let's be even more clear - let's say you want only the button click handler to engage"...why? Why do I want only the button click to engage? I don't ask what stopPropagation do, you are attempting over and over to tell me what ti does. Please read my question that I state clearly when to use it. – KT-mongo Feb 21 '19 at 20:57
-
@Kleo I have stated it clearly. You want only the button click to engage, not the rest. You do still want to handle clicks to the background. Why - I can't tell you because when you get to that situation, you'd know. I can make up a reason, one that sounds plausible and you are very likely to encounter but you since I expect you to just reject it, so I won't bother. You keep saying I'm not giving you a reason but you've not given me a question, either. It's like asking "when do I need to use a `for` loop" and then "but why would I use `for` loop" when I give you an example. – VLAZ Feb 21 '19 at 21:02
-
1"Why - I can't tell you because when you get to that situation, you'd know." and " I can make up a reason, one that sounds plausible" really doesn't go hand in hand with "I have stated it clearly", anyway please submit your comments as an answer and not as a comment if you believe you were clear enough. – KT-mongo Feb 21 '19 at 21:10
1 Answers
This really shouldn't be an answer, but there is only so much you can write in a single comment
I don't think you're doing your question justice by having the words "good practice" in its title. This sort of implies that in most cases, stopPropagation
is bad practice. This is similar to saying that eval is evil. It completely brushes off any legitimate use cases of it with misplaced dogmatism.
I never found myself in a situation where using stopPropagation
didn't feel like a workaround to avoid fixing the real issue.
In an ideal world, applications are built out of smaller components that do very little on their own but are highly reusable and combinable. For this to work, the recipe is simple yet very difficult to execute: each component must know nothing about the outside world.
Therefore if a component needs to use stopPropagation()
, it can only be because it knows that something further up the chain will break or that it will put your application into an undesirable state.
In this case you should be asking yourself whether that is not a symptom of a design issue. Perhaps you need a component that orchestrates and manages the events of its children?
You should also consider the fact that preventing the propagation of events can cause other components to misbehave. The classic example is a drop-down that closes when you click outside of it. If that click is stopped, your drop-down may never close.
Think of events as sources of data. You don't want to lose data. Au contraire! Let it go, let it free ;)
While I don't see using stopPropagation
as bad or evil practice, I just don't think it is ever needed.
Example: how to avoid using stopPropagation
In this example we're building a very simple game: if you click on a red area you lose, on a green area you win. The game is over once a click is made.
const onClick = (selector, handler) => {
document.querySelector(selector).addEventListener('click', handler);
};
onClick('#game', () => console.log('game over'));
onClick('#red', () => console.log('you lost'));
onClick('#green', () => console.log('you won'));
#red, #green { width: 50px; height: 50px; display: inline-block; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
<div id="red"></div>
<div id="green"></div>
</div>
Now let's imagine that there are different levels in which red and green blocks are arranged randomly. In level #42, the red block contains the green one.
const onClick = (selector, handler) => {
document.querySelector(selector).addEventListener('click', handler);
};
onClick('#game', () => console.log('game over'));
onClick('#red', () => console.log('you lost'));
onClick('#green', () => console.log('you won'));
#red, #green { max-width: 100px; padding: 10px; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
<div id="red">
<div id="green"></div>
</div>
</div>
As you can see when you click on the green area, you both win and lose at the same time! And if you were to put a stopPropagation()
call in the green handler, there will be no way to win this game since the click won't bubble up to the game handler to signal the end of the game!
Solution 1: identify the origin of the click
const filter = handler => ev =>
ev.target === ev.currentTarget ? handler(ev) : null;
const onClick = (selector, handler) => {
document.querySelector(selector).addEventListener('click', handler);
};
onClick('#game', () => console.log('game over'));
onClick('#red', filter(() => console.log('you lost')));
onClick('#green', () => console.log('you won'));
#red, #green { max-width: 100px; padding: 10px; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
<div id="red">
<div id="green"></div>
</div>
</div>
The key function is filter
. It will make sure that handler
will only execute if the click actually originated from the node itself and not from one of its children.
The currentTarget read-only property of the Event interface identifies the current target for the event, as the event traverses the DOM. It always refers to the element to which the event handler has been attached, as opposed to Event.target, which identifies the element on which the event occurred.
https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
Solution 2: use event delegation
You don't actually need three events handlers. Just set up one on the #game
node.
const onClick = (selector, handler) => {
document.querySelector(selector).addEventListener('click', handler);
};
onClick('#game', (ev) => {
if (ev.target.id === 'red') {
console.log('you lost');
} else if (ev.target.id === 'green') {
console.log('you won');
}
console.log('game over');
});
#red, #green { max-width: 100px; padding: 10px; }
#red { background-color: orangered; }
#green { background-color: yellowgreen; }
<div id="game">
<div id="red">
<div id="green"></div>
</div>
</div>

- 17,580
- 5
- 58
- 84
-
2Stop propagation is needed when you have JavaScript running on the same event of nested elements. Imagine having a click event on a parent element AND a child. If you clicked the child, and don’t want it to also count as a click for the parent, then you need to stop propagation in the child click handler. This isn’t common, but at times it’s necessary. – Nate Feb 21 '19 at 23:40
-
1It should be relatively easy to figure out the origin of the click though. With that the parent can decide whether it can ignore that click or not. I’m happy to demonstrate this if you think that’d be useful. – customcommander Feb 22 '19 at 06:40
-
1I guess I’m trying to understand the purpose of the parent realizing that the click was intended for the child, vs the child being like “I got this” and stopping propagation. It just feels like a specificity thing. Either way, the two need to be aware of each other to know that only one of them should handle the click. Can you explain the purpose of avoiding stopPropagation()? Currently I’m stuck thinking “The feature exists for a reason. Handling it in the parent is overcomplicated and probably slower.”. I’d love to understand what you’re saying. – Nate Feb 22 '19 at 13:44
-
1@Nate I have edited my answer to include some examples to show how to avoid using `stopPropagation()`. I hope that you (and @Kleo) find them useful. – customcommander Feb 22 '19 at 21:37
-
I am confused on the logic for example 1. Can you break it down step by step? I think it mainly has to do with the syntax of all the statements. Example 2 makes perfect sense... for the most part. In example 2, I don't understand why in the bubble up why the event wouldn't be fired anyways, meaning: 1. #game is clicked (let's say on #green), so, if I understand correctly, it should go #green, #red, #game, which would mean, 2. #green would be fired, then #red, then #game, which would mean, 3. it would still display win, lost, then no result. Maybe I don't understand events well enough. – Shmack Aug 28 '20 at 23:24
-
Had to remind myself that "onclick" wouldn't fire that function multiple times, because those elements don't have the event handler attached to them, in response to my question in example 2. That being said a full explanation (even explaining some of the syntax) might be helpful. Other than that, spectacular answer. Left an upvote for you :D – Shmack Aug 28 '20 at 23:34