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.
Related
So, i wondered, why this code doesn't work properly, and what can i do, to prevent such a behaviour:
If I would need to prevent event propagation of parent, whilst particular child got clicked, i used method 1, but it seems not to be working, but method 2 is working fine though.
//method 1
$(document).on({
click: function(e) {
console.log('clicked!');
e.preventDefault();
return false;
}
}, '.hax');
//method 2
/*$('.hax').on('click', function(e){
e.preventDefault();
return false;
});*/
//uncommenting will prevent event propagation
.hax {
background-color: whitesmoke;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='wrapper' onclick='alert("hello")'>
<div class='hax'>hax!</div>
</div>
Method 1 Is using event delegation ,so in it event is not directly bind with the element , its bound with the parent ,So in your case the parent is document . in this the case whatever event will be fired for that particular element it will be tracked down from the DOM tree and will execute the parent call before. In your case it will first call the alert from parent .
In method 2 - event is directly bound with the element , The event of parent will still got fired unless you prevent that in the handler but since the handler is bound to the target , you will not face any other action(alert in your case)
Get better Idea of
Event Delegation
You are creating an event delegation by method 1, which can be created the following way too:
$(document).on('click', '.hax', function (e) {
console.log('clicked!');
e.preventDefault();
return false;
});
For clarifying event delegation briefly:
Understanding how events propagate is an important factor in being able to leverage Event Delegation. Any time one of our anchor tags is clicked, a click event is fired for that anchor, and then bubbles up the DOM tree(Up to DOM top), triggering each of its parent click event handlers.
It does not mean you can't achieve your goal here with this method, but in order to make it work, you can create a middle parent for div.hax which is descendant of div.wrapper. I mean:
<div class='wrapper' onclick='alert("hello")'>
<div id="stopHere">
<div class='hax'>hax!</div>
</div>
</div>
Now, we can use method 1, but we only need to stop event propagation / event delegation before it reach div.wrapper. Thus in our newly added div#stopHere:
$("div#stopHere").on('click', '.hax', function (e) {
console.log('clicked!');
e.preventDefault();
return false;
});
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
i got a problem
<div id='parent'>
<div id='child'>
</div>
</div>
what i want is when the child is clicked addClass,and when the rest of parent is clicked removeClass,so when i try to do
$('#child').click(function(){
$(this).addClass();
})
$('#parent').click(function(){
$('#child').removeClass();
})
its not working i think its because the child is actually inside the parent,so when the child is clicked the parent clicked right?
so how can i do that?
try this:
$('#child').click(function(evt){
evt.stopPropagation();
$(this).addClass("myClass");
});
You could use event.stopPropagation to prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
$('#child').click(function(e){
e.stopPropagation();
$(this).addClass();
});
Several users have already suggested a good solution - here's an explanation of why it works:
When you click an HTML element (actually a DOM object...), the click event "bubbles" all the way up to the root element. For example, a click in #child also triggers a click in #parent, as you expected.
To stop this behavior, you need to call .stopPropagation() on the click event - that will tell the browser that you do not want the event to propagate, but keep it "local". Basically, when you've handled it here, you're done with it and don't want to see it again.
Conveniently, jQuery event handlers take the event as the first argument, so if you assign any function with the signature function (e) { ... }, you can stop event propagation by e.stopPropagation(); as others have suggested. In your case, you want
$('#child').click(function(e){
$(this).addClass();
e.stopPropagation();
});
$('#parent').click(function(){
$('#child').removeClass();
});
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>
I am trying to stop some events but stopPropagation does not work with "live" so I am not sure what to do. I found this on their site.
Live events do not bubble in the
traditional manner and cannot be
stopped using stopPropagation or
stopImmediatePropagation. For example,
take the case of two click events -
one bound to "li" and another "li a".
Should a click occur on the inner
anchor BOTH events will be triggered.
This is because when a
$("li").bind("click", fn); is bound
you're actually saying "Whenever a
click event occurs on an LI element -
or inside an LI element - trigger this
click event." To stop further
processing for a live event, fn must
return false
It says that fn must return false so what I tried to do
$('.MoreAppointments').live('click', function(e) {
alert("Hi");
return false;
});
but that did not work so I am not sure how to make it return false.
Update
Here is some more information.
I have a table cell and I bind a click event to it.
$('#CalendarBody .DateBox').click(function(e)
{
AddApointment(this);
});
So the AddApointment just makes some ui dialog box.
Now the live code(MoreAppointments) sits in this table cell and is basically an anchor tag. So when I click on the anchor tag it first goes to the above code(addApointment - so runs that event first) runs that but does not launch my dialog box instead it goes straight to the (MoreAppointment) event and runs that code. Once that code has run it launches the dialog box from "addApointment".
Update 2
Here is some of the html. I did not copy the whole table since it is kinda big and all the cells repeat itself with the same data. If needed I will post it.
<td id="c_12012009" class="DateBox">
<div class="DateLabel">
1</div>
<div class="appointmentContainer">
<a class="appointments">Fkafkafk fakfka kf414<br />
</a><a class="appointments">Fkafkafk fakfka kf414<br />
</a><a class="appointments">Fkafkafk fakfka kf414<br />
</a><a class="appointments">Fkafkafk fakfka kf414<br />
</a><a class="appointments">Fkafkafk fakfka kf414<br />
</a>
</div>
<div class="appointmentOverflowContainer">
<div>
<a class="MoreAppointments">+1 More</a></div>
</div>
</td>
The short answer is simply, you can't.
The problem
Normally, you can stop an event from "bubbling up" to event handlers on outer elements because the handlers for inner elements are called first. However, jQuery's "live events" work by attaching a proxy handler for the desired event to the document element, and then calling the appropriate user-defined handler(s) after the event bubbles up the document.
(source: shog9.com)
This generally makes "live" binding a rather efficient means of binding events, but it has two big side-effects: first, any event handler attached to an inner element can prevent "live" events from firing for itself or any of its children; second, a "live" event handler cannot prevent any event handlers attached directly to children of the document from firing. You can stop further processing, but you can't do anything about processing that has already occurred... And by the time your live event fires, the handler attached directly to the child has already been called.
Solution
Your best option here (so far as I can tell from what you've posted) is to use live binding for both click handlers. Once that's done, you should be able to return false from the .MoreAppointments handler to prevent the .DateBox handler from being called.
Example:
$('.MoreAppointments').live('click', function(e)
{
alert("Hi");
return false; // prevent additional live handlers from firing
});
// use live binding to allow the above handler to preempt
$('#CalendarBody .DateBox').live('click', function(e)
{
AddApointment(this);
});
I've used such kind if code and it worked for me:
$('#some-link').live('click', function(e) {
alert("Link clicked 1");
e.stopImmediatePropagation();
});
$('#some-link').live('click', function(e) {
alert("Link clicked 2");
});
so, it seems to me, that now JQuery support stopImmediatePropagation with live events
Maybe you could check that the click event didn't occur on an a element:
$('#CalendarBody .DateBox').click(function(e) {
// if the event target is an <a> don't process:
if ($(e.target).is('a')) return;
AddApointment(this);
});
Might Work?
I'm using this:
if(event.target != this)return; // stop event bubbling for "live" event
I use
e.stopPropagation(); // to prevent event from bubbling up
e.preventDefault(); // then cancel the event (if it's cancelable)
I've used this in certain situations. Note: not always applicable, so assess for your needs as always:
html:
Click me
js (in your live event handler):
if(e.target.className == 'my-class-name') {
e.preventDefault();
// do something you want to do...
}
This way, my live event only 'runs' when a particular element type/classname attr is clicked.
The e.preventDefault() here is to stop the link I'm clicking moving the scroll-position to the top of the page.
Simply use **"on"** function to bind click event of child as well as parent element.
Example : $("#content-container").on("click","a.childElement",function(e){
alert("child clicked");
e.stopPropagation() ;
});
$("#content-container").on("click","div.parentElement",function(e){
alert("parent clicked");
});
( where content-container is the outer div containing both parent as well as child elements. )
Here only "child clicked" alert will occur.
Thanks.