1

Please tell me why the e.stopPropagation() function does not work?

$(document).ready(function() {
  var el = '<div class="el" onclick="alert(1);"><div class="stop">X</div>Click</div>';
  $("body").append(el);
})
$(document).on("click", ".stop", function(e) {
  e.stopPropagation();
  e.preventDefault();
  alert(2);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
</body>
Vans
  • 185
  • 1
  • 8
  • It also does not prevent immediate propagation to other event-handlers. If you want to stop those, see `stopImmediatePropagation().` thats what the documentation says – Chris G Feb 23 '23 at 11:54
  • I believe [this](https://stackoverflow.com/a/5963688/13927534) answers your question? – Miss Skooter Feb 23 '23 at 11:54
  • Does this answer your question? [What's the difference between event.stopPropagation and event.preventDefault?](https://stackoverflow.com/questions/5963669/whats-the-difference-between-event-stoppropagation-and-event-preventdefault) – Miss Skooter Feb 23 '23 at 11:57
  • @ChrisG Does not work – Vans Feb 23 '23 at 11:58

2 Answers2

4

why the e.stopPropagation() function does not work?

It does work. Observe:

$(document).on("click", ".outer", function(e) {
  alert(3);
});

$(document).on("click", ".el", function(e) {
  e.stopPropagation();
  alert(2);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
  <div class="el" onclick="alert(1)";>Click</div>
</div>

In your code the event propagation is indeed being stopped. However, there are no further event handlers up the DOM tree so there's no propagation to observe in the first place. What you have are two separate click handling events on the same element, both of which are being processed independently.

In the example shown here, a parent element (div.outer) does have an event handler, and the propagation stops before it reaches that handler. Without stopping propagation, you'd see that event handler invoked:

$(document).on("click", ".outer", function(e) {
  alert(3);
});

$(document).on("click", ".el", function(e) {
  alert(2);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
  <div class="el" onclick="alert(1)";>Click</div>
</div>

You might be looking for stopImmediatePropagation() instead? This will prevent further event handlers from being invoked on the same element. For example:

$(document).on("click", ".el", function(e) {
  e.stopImmediatePropagation();
  alert(2);
});

$(document).on("click", ".el", function(e) {
  e.stopImmediatePropagation();
  alert(3);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="el" onclick="alert(1)";>Click</div>

In this case The second click handler on the target element stopped further click handlers on that same element from being processed. Note that this has no effect on the first click handler, which was already invoked by then.


Edit: Your updated code demonstrates a slightly different problem. (Which was being obscured in the original code by the original problem addressed above.)

Notice how you are attaching your jQuery event handler. Which DOM object are you attaching it to?... document. Which is much further up the DOM hierarchy and isn't reached until after the parent element has already processed its inline click handler.

If you attach a click handler directly to the element, you can see the difference:

$(".stop").on("click", function(e) {
  e.stopPropagation();
  alert(2);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="el" onclick="alert(1)">
  <div class="stop">X</div>
  Click
</div>

Another edit for the ongoing moving target...

You're still essentially running into the same issue. One click handler is on the element itself, another is on the document object. The latter won't be processed before the former.

Appending the elements to the DOM after the fact may indeed complicate things a little in this case, and you may need to attach the click handler to the element(s) themselves after appending them. For example, this stops the propagation:

$(document).ready(function() {
  var el = '<div class="el" onclick="alert(1);"><div class="stop">X</div>Click</div>';
  $("body").append(el);
  
  $(".stop").on("click", function(e) {
    e.stopPropagation();
    alert(2);
  });
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
</body>

The key difference here is that the jQuery click handler isn't being attached to the document or relying on event delegation/propagation in the first place. It's being attached directly to the target element, and as such needed to be attached after the element is added to the DOM.

David
  • 208,112
  • 36
  • 198
  • 279
  • What are these new elements? Can't you see my code? – Vans Feb 23 '23 at 11:57
  • @Vans: Indeed I can see your code. I even tested it and copied it to this answer. I then added a parent element (`div.outer`) to demonstrate what `stopPropagation()` does and how it works. In your code there's no event handler further up the DOM hierarchy to observe, so there's no observable difference between an event which stopped propagating and one which did not. I added such an element so the difference can be observed. – David Feb 23 '23 at 11:59
  • I don't need alert(1); – Vans Feb 23 '23 at 12:04
  • @Vans: I've updated the answer. Though it looks like [another answer to the updated question](https://stackoverflow.com/a/75544692/328193) also addressed the new issue. – David Feb 23 '23 at 12:19
  • Is there a way to do this with jquery? I use this handler because '.el' elements are added with append. – Vans Feb 23 '23 at 12:21
  • @Vans: Updated the answer. Though there's no guarantee that this will also address whatever else you're going to edit into the question next... – David Feb 23 '23 at 12:34
  • New elements may appear later, after the page is loaded. – Vans Feb 23 '23 at 12:37
  • @Vans: Indeed. And with jQuery alone I suspect you'd need to attach the `click` handlers directly to those appended elements, which of course would need to happen after they are appended as in the most recent example in this answer. It's possible to dig further into the event capture as demonstrated in [this answer](https://stackoverflow.com/a/75544692/328193) using native functionality, but unless I'm missing something I don't see an option in jQuery to do that. What you have in the problem is a mix of jQuery and native functionality, so an elegant solution would likely involve the same. – David Feb 23 '23 at 12:45
  • Understood. Thanks for the answer. I will globally change the code so that there is one processing. – Vans Feb 23 '23 at 12:55
2

First, you made a typo. class only has 2 ss in it.

That aside, the event starts on the div that you click. It then propagates to the div with the onclick attribute (triggering that), then continues up the DOM until it hits the document object and triggers the event handler bound with jQuery at which point stopPropagation is called and it stops.

If you want to prevent the onclick attribute triggering then you need to handle the event in the capture phase. I don't think jQuery supports that so you'll need to use native DOM instead.

document.addEventListener("click", function(e) {
  if (!e.target.closest(".stop")) return;
  e.stopPropagation();
  alert(2);
}, {
  capture: true
});
<div class="el" onclick="alert(1)" ;>
  <div class="stop">X</div>
  Click
</div>
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335