jQuery.on() fails to work with stopPropagation - javascript

I want to stop func() from being executed when I click the inner div.
this is the html:
<div onclick="func()">
<div id="inner-div">
I'm an inner div
</div>
</div>
This code will stop propagation:
$('#inner-div').bind("click", function(e){
e.stopPropagation();
})
This code fails to stop propagation:
$(document).on("click", '#inner-div' , function(e){
e.stopPropagation();
})
How do I do it?

Stopping propagation from a delegated handler will not work, since the event has already propagated up the document tree in order for that delegated handler to run.
This is mentioned in the documentation:
Since the .live() method handles events once they have propagated to
the top of the document, it is not possible to stop propagation of
live events.
on() and delegate() behave the same as live() in this context, only with an ancestor element instead of the document itself. In all cases, it is too late to stop the event from propagating from the target element.

And to solve it you could do:
<div onclick="func(event, this)">
<div id="inner-div">
I'm an inner div
</div>
</div>
JS
function func(e, that) {
if (e.target === that) {
// do stuff
}
}
FIDDLE
or use jQuery and remove the inline handler (as the question is tagged jQuery)
<div id="parent">
<div id="inner-div">
I'm an inner div
</div>
</div>
JS
$(document).on('click', '#parent', function(e) {
if (e.target === this) {
// do stuff
}
});
FIDDLE

Related

jQuery .on() delegation with selector that matches nested elements

// Example A
$("#delegate").on("click", function(event) {
// executes on click on any descendant of #delegate or self (not a delegate at all)
// 'this' is #delegate
});
// Example B
$("#delegate").on("click", "#outer", function(event) {
// executes on click on any descendant of #outer or self
// 'this' is #outer or #inner (depends on the actual click)
});
$("#delegate").on("click", "#inner", function(event) {
// executes on click on any descendant of #inner or self (nothing happens when clicking #outer)
// 'this' is #inner
});
// Example C (now it's getting weird)
$("#delegate").on("click", "div", function(event) {
// executes twice, when clicking #inner, because the event passes #outer when bubbling up
// one time 'this' is #inner, and the other time 'this' is #outer
// stopPropagation() // actually prevents the second execution
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="delegate">
<div id="outer">
outer
<div id="inner">
inner
</div>
</div>
</div>
How do you explain this behaviour logically?
There is exactly one click event, which starts on #inner, bubbles through #outer and finally reaches #delegate.
The event is catched (exactly) once by the #delegate's on-handler. The handler checks if event's history contains any div elements.
If this applies, the callback function should be called once. That's what I would expect. "Single Event, single Handler, single Condition, single Callback".
It gets more crazy if you take a look at the stopPropagation() behaviour. You can actually avoid the second execution, though the event has already reached #delegate. stopPropagation(should not work here.
What kind of "magic" is done in the implementation of the on-delegation logic? Do event-bubbling and program flow split up in any way?
Please don't post "practical advice" in the first place ("Use xyz instead!"). I'd like to understand why the code works the ways it does.
As you have bound events on all the divs in two ways:
Using id attribute of the element div.
Using the tag name of the element div.
so if you event.stopPropagation(); on the last one still the alert will come two times because you have cliked the div and also the #inner (for instance.)
Check the snippet below.
$("#delegate").on("click", function(event) {
alert(this.id);
});
/*
$("#delegate").on('click', "#outer", function(event) {
alert(this.id);
});
$("#delegate").on('click', "#inner", function(event) {
alert(this.id);
});
*/
// now it's getting weird
$("#delegate").on("click", "div", function(event) {
event.stopPropagation();
alert(this.id);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="delegate">
<div id="outer">
outer
<div id="inner">
inner
</div>
</div>
</div>
The way event delegation works is that jQuery walks up the DOM tree from the innermost target to the element that the delegation was bound to, testing each element to see if it matches the selector. If it does, it executes the handler with this bound to that element.
When event.stopPropagation is called, it sets a flag in the event object. The loop that walks the DOM tree also calls event.isPropagationStopped(). If propagation is stopped, it breaks out of the loop.
In other words, jQuery is doing its own bubbling and propagation stopping when it implements delegation, it's not making use of the browser's bubbling (except that this bubbling is necessary for the initial event to be triggered on #delegate, so that the jQuery loop will run).
Everything works as expected. Have a look at this fiddle:
$("#delegate").on("click", "div", function(event) {
// event.stopPropagation();
alert('div: ' + $(this).attr('id'));
});
Clicking #inner will fire the above event. The event will bubble up to #outer since #inner is a descendent of #outer. Since #outer also is a <div, the event will be fired on #outer, also. Logically, clicking on #inner will first alert "div: inner" and then "div: outer".
Calling event.stopPropagation() tells the event not to bubble up, so #outer stays uninvoked.
Other than in this fiddle:
<div id="delegate">
<div id="first">
first
</div>
<div id="second">
second
<div id="third">
third, inside second
</div>
</div>
</div>
Clicking on third will first alert third, then second and then stop, because #first is not a parent but a sibling.

How to add a click method inside of element with click method?

Suppose I have:
<div id="outer" onclick="thingsHappen()">
<div id="inner"></div>
</div>
When I click on outer or inner div, thingsHappen() is executed. That is obvious.
Now I have got a need to define a different method for the inner div.
For example
$("#inner").click(function() {
doThings();
});
When I click on inner both thingsHappen() and doThings() executes.
How do I execute doThings() when I click on inner div without executing thingsHappen()?
I tried to unbind click method from #inner, but it did not work.
PS. I cannot change the structure of HTML.
Stop the propagation of the event:
$("#inner").click(function(e) {
doThings();
e.stopPropagation();
});
Example: http://jsfiddle.net/QNt76/
JavaScript events bubble up the DOM tree unless you stop them from propagating. This is what was causing the parent event handler to get notified.
You want Event.stopPropagation():
$("#inner").click(function(e) {
doThings();
e.stopPropagation();
});
Events pertaining to a child element bubble up to parent elements in the DOM unless propagation is stopped like so:
$("#inner").click(function(event) {
doThings();
event.stopPropagation();
});
Here is a good read on capturing/bubbling and Javascript events. http://www.quirksmode.org/js/events_order.html
$("#inner").click(function(e) {
e.stopPropagation();
doThings();
});​
What you are trying to do is stop the event (click) from "bubbling" up. In this case, you would want to stop the propagation of the event in the bubbling phase. If you are using jquery, you can use this function:
HTML
<div id="outer" onclick="thingsHappenOuter()">
<div id="inner">
</div>
</div>
JS
$("#inner").click(function(event) {
event.stopPropagation();
// do something
});
SEE: http://api.jquery.com/event.stopPropagation/ for more information.
You have to stop the propagation to the Document Tree:
$("#inner").click(function(event) {
doThings();
event.stopPropagation();
});
See: http://api.jquery.com/event.stopPropagation/
Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.

delegate on a child element

Suppose I have the following HTML:
<div class="container">
<span class="remove">remove</span>
</div>
and jquery:
$(".container").delegate(".remove", "click", function() {
alert('yes');
});
This works, but now I have HTML like:
<div class"container">
<div>
<span class="remove">remove</span>
</div>
</div>
or even
<div class"container">
<div>
<div>
<span class="remove">remove</span>
</div>
</div>
</div>
So how can I make the jquery work in this case, so that if finds the .remove element in a hierarchy of elements and make the click event work?
NOTE: I'm adding the elements dynamically (using clone function)!
This will still work. As long as the element you're delegating the event to is an ancestor of the element, the element will receive the event.
An exception to this is where an element which is a more direct ancestor stops the propagation of the event using event.stopPropagation(), event.stopImmediatePropagation(), or by returning false in it's handler;
$(".container").delegate(".remove", "click", function() {
// This will never get executed, as the first handler cancels the propagation.
alert('yes');
});
$(".container > div").delegate(".remove", "click", function(e) {
// This handler get the event first as it is closer to the source of the event.
e.stopPropagation();
});
FYI, if you're using jQuery > 1.7, you should consider using the new on() method as on() was implemented to disperse the confusion over live(), delegate() and bind(). It is expected that these older methods will be depreciated in 1.8. The following is equivilent for what you're currently using;
$(".container").on("click", ".remove", function(e) {
alert('yes');
});

stopPropagation vs. stopImmediatePropagation

What's the difference between event.stopPropagation() and event.stopImmediatePropagation()?
stopPropagation will prevent any parent handlers from being executed stopImmediatePropagation will prevent any parent handlers and also any other handlers from executing
Quick example from the jquery documentation:
$("p").click(function(event) {
event.stopImmediatePropagation();
});
$("p").click(function(event) {
// This function won't be executed
$(this).css("background-color", "#f00");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>example</p>
Note that the order of the event binding is important here!
$("p").click(function(event) {
// This function will now trigger
$(this).css("background-color", "#f00");
});
$("p").click(function(event) {
event.stopImmediatePropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>example</p>
Surprisingly, all other answers only say half the truth or are actually wrong!
e.stopImmediatePropagation() stops any further handler from being called for this event, no exceptions
e.stopPropagation() is similar, but does still call all handlers for this phase on this element if not called already
What phase?
E.g. a click event will always first go all the way down the DOM (called “capture phase”), finally reach the origin of the event (“target phase”) and then bubble up again (“bubble phase”). And with addEventListener() you can register multiple handlers for both capture and bubble phase independently. (Target phase calls handlers of both types on the target without distinguishing.)
And this is what the other answers are incorrect about:
quote: “event.stopPropagation() allows other handlers on the same element to be executed”
correction: if stopped in the capture phase, bubble phase handlers will never be reached, also skipping them on the same element
quote: “event.stopPropagation() [...] is used to stop executions of its corresponding parent handler only”
correction: if propagation is stopped in the capture phase, handlers on any children, including the target aren’t called either, not only parents
...and: if propagation is stopped in the bubble phase, all capture phase handlers have already been called, including those on parents
A fiddle and mozilla.org event phase explanation with demo.
A small example to demonstrate how both these propagation stoppages work.
var state = {
stopPropagation: false,
stopImmediatePropagation: false
};
function handlePropagation(event) {
if (state.stopPropagation) {
event.stopPropagation();
}
if (state.stopImmediatePropagation) {
event.stopImmediatePropagation();
}
}
$("#child").click(function(e) {
handlePropagation(e);
console.log("First event handler on #child");
});
$("#child").click(function(e) {
handlePropagation(e);
console.log("Second event handler on #child");
});
// First this event will fire on the child element, then propogate up and
// fire for the parent element.
$("div").click(function(e) {
handlePropagation(e);
console.log("Event handler on div: #" + this.id);
});
// Enable/disable propogation
$("button").click(function() {
var objectId = this.id;
$(this).toggleClass('active');
state[objectId] = $(this).hasClass('active');
console.log('---------------------');
});
div {
padding: 1em;
}
#parent {
background-color: #CCC;
}
#child {
background-color: #000;
padding: 5em;
}
button {
padding: 1em;
font-size: 1em;
}
.active {
background-color: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">
<div id="child"> </div>
</div>
<button id="stopPropagation">Stop Propogation</button>
<button id="stopImmediatePropagation" ">Stop Immediate Propogation</button>
There are three event handlers bound. If we don’t stop any propagation, then there should be four alerts - three on the child div, and one on the parent div.
If we stop the event from propagating, then there will be 3 alerts (all on the inner child div). Since the event won’t propagate up the DOM hierarchy, the parent div won’t see it, and its handler won’t fire.
If we stop propagation immediately, then there will only be 1 alert. Even though there are three event handlers attached to the inner child div, only 1 is executed and any further propagation is killed immediately, even within the same element.
I am a late comer, but maybe I can say this with a specific example:
Say, if you have a <table>, with <tr>, and then <td>. Now, let's say you set 3 event handlers for the <td> element, then if you do event.stopPropagation() in the first event handler you set for <td>, then all event handlers for <td> will still run, but the event just won't propagate to <tr> or <table> (and won't go up and up to <body>, <html>, document, and window).
Now, however, if you use event.stopImmediatePropagation() in your first event handler, then, the other two event handlers for <td> WILL NOT run, and won't propagate up to <tr>, <table> (and won't go up and up to <body>, <html>, document, and window).
Note that it is not just for <td>. For other elements, it will follow the same principle.
event.stopPropagation will prevent handlers on parent elements from running.
Calling event.stopImmediatePropagation will also prevent other handlers on the same element from running.
From the jQuery API:
In addition to keeping any additional
handlers on an element from being
executed, this method also stops the
bubbling by implicitly calling
event.stopPropagation(). To simply
prevent the event from bubbling to
ancestor elements but allow other
event handlers to execute on the same
element, we can use
event.stopPropagation() instead.
Use
event.isImmediatePropagationStopped()
to know whether this method was ever
called (on that event object).
In short: event.stopPropagation() allows other handlers on the same element to be executed, while event.stopImmediatePropagation() prevents every event from running.
1)event.stopPropagation():
=>It is used to stop executions of its corresponding parent handler only.
2) event.stopImmediatePropagation():
=> It is used to stop the execution of its corresponding parent handler and also handler or function attached to itself except the current handler.
=> It also stops all the handler attached to the current element of entire DOM.
Here is the example: Jsfiddle!
Thanks,
-Sahil
Here is a demo to illustrate the difference:
document.querySelectorAll("button")[0].addEventListener('click', e=>{
e.stopPropagation();
alert(1);
});
document.querySelectorAll("button")[1].addEventListener('click', e=>{
e.stopImmediatePropagation();
alert(1);
});
document.querySelectorAll("button")[0].addEventListener('click', e=>{
alert(2);
});
document.querySelectorAll("button")[1].addEventListener('click', e=>{
alert(2);
});
<div onclick="alert(3)">
<button>1...2</button>
<button>1</button>
</div>
Notice that you can attach multiple event handlers to an event on an element.
event.stopPropagation() allows other handlers on the same element to be executed, while event.stopImmediatePropagation() prevents every event from running. For example, see below jQuery code block.
$("p").click(function(event)
{ event.stopImmediatePropagation();
});
$("p").click(function(event)
{ // This function won't be executed
$(this).css("color", "#fff7e3");
});
If event.stopPropagation was used in previous example, then the next click event on p element which changes the css will fire, but in case event.stopImmediatePropagation(), the next p click event will not fire.
Here I am adding my JSfiddle example for stopPropagation vs stopImmediatePropagation.
JSFIDDLE
let stopProp = document.getElementById('stopPropagation');
let stopImmediate = document.getElementById('stopImmediatebtn');
let defaultbtn = document.getElementById("defalut-btn");
stopProp.addEventListener("click", function(event){
event.stopPropagation();
console.log('stopPropagation..')
})
stopProp.addEventListener("click", function(event){
console.log('AnotherClick')
})
stopImmediate.addEventListener("click", function(event){
event.stopImmediatePropagation();
console.log('stopimmediate')
})
stopImmediate.addEventListener("click", function(event){
console.log('ImmediateStop Another event wont work')
})
defaultbtn.addEventListener("click", function(event){
alert("Default Clik");
})
defaultbtn.addEventListener("click", function(event){
console.log("Second event defined will also work same time...")
})
div{
margin: 10px;
}
<p>
The simple example for event.stopPropagation and stopImmediatePropagation?
Please open console to view the results and click both button.
</p>
<div >
<button id="stopPropagation">
stopPropagation-Button
</button>
</div>
<div id="grand-div">
<div class="new" id="parent-div">
<button id="stopImmediatebtn">
StopImmediate
</button>
</div>
</div>
<div>
<button id="defalut-btn">
Normat Button
</button>
</div>

Howto: div with onclick inside another div with onclick javascript

just a quick question. I'm having a problem with divs with onclick javascript within each other. When I click on the inner div it should only fire it's onclick javascript, but the outer div's javascript is also being fired. How can the user click on the inner div without firing the outer div's javascript?
<html>
<body>
<div onclick="alert('outer');" style="width:300px;height:300px;background-color:green;padding:5px;">outer div
<div onclick="alert('inner');" style="width:200px;height:200px;background-color:white;" />inner div</div>
</div>
</div>
</body>
</html>
Basically there are two event models in javascript. Event capturing and Event bubbling. In event bubbling, if you click on inside div, the inside div click event fired first and then the outer div click fired. while in event capturing, first the outer div event fired and than the inner div event fired. To stop event propagation, use this code in your click method.
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
Check out the info on event propagation here
In particular you'll want some code like this in your event handlers to stop events from propagating:
function myClickHandler(e)
{
// Here you'll do whatever you want to happen when they click
// now this part stops the click from propagating
if (!e) var e = window.event;
e.cancelBubble = true;
if (e.stopPropagation) e.stopPropagation();
}
This is a case of event bubbling.
You can use
e.cancelBubble = true; //IE
and
e.stopPropagation(); //FF
Just add this code :
window.event.stopPropagation();
return false; from the inner div's onclick function:
<div onclick="alert('inner'); return false;" ...
What you're dealing with is called event propagation.
One more way for webkit based browsers:
<div onclick="alert('inner'); event.stopPropagation;" ...
This worked for me in Jquery:
$('#outer_element').click(function(event) {
if(event.target !== event.currentTarget) return;
alert("Outer element is clicked")
});
This way, if the current target is not the same as the outer div, it will do nothing. You can implement normal functions on the child elements.
Here is some more reference to help you in understanding javascript event bubbling.
you have two 'div' and three '/div'.
This was very helpful, but it didn't work for me.
What i did is described here.
So I put a condition to the outer onclick event:
if( !event.isPropagationStopped() ) {
window.location.href = url;
}
You can use
$("divOrClassThatYouDontWantToPropagate").click(function( event ) {
event.stopPropagation();
});
//Using window.event.stopPropagation(), the parent div wont be clicked when the child div is clicked
//HTML
<div onclick="parent_func()">
<div onclick="child_func()"></div>
</div>
//JS
function parent_func(){
window.event.stopPropagation();
console.log('Parent')
}
function child_func(){
window.event.stopPropagation();
console.log('Child')
}

Categories

Resources