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();
});
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;
});
When a particular element is clicked, I want to call a function. When any of its children are clicked, I do not want to call the function.
I am not using jQuery.
Example:
I created a modal:
<div class="fullscreen-overlay">
<div class="card">
...
</div>
</div>
I want to call my closeModal() function when ".fullscreen-overlay" is clicked, but not when ".card" or any of its content are clicked.
jsfiddle:
https://jsfiddle.net/5kuwmf9s/
Research:
I could've sworn there was a CSS attribute for this, but after Googling and searching on SO for it, I can't find it / might've been imagining it. "pointer-events" stops it on the target element, not bubbling.
Theres another answer that suggests attaching an event handler to ALL children that catches their events and stops propagation - which seems unnecessary. My children are dynamic, and this will get complicated to keep attaching handlers.
You might have compared event's target and currentTarget
They would be equal only if the current element is the one that was the initial source of the event:
function handleClick(e){
if (e.target === e.currentTarget) {
alert('clicked!');
}
}
References:
https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget
https://developer.mozilla.org/en-US/docs/Web/API/Event/target
JSFiddle: https://jsfiddle.net/u78k4k6t/
try use an element and style it as your '.fullscreen-overlay' and bind event to this element
as the answer link shows
use event.target or event.srcElement
I want to do something on all clicks except on a certain element.
I've created a very simple example which demonstrates the issue: http://jsfiddle.net/nhe6wk77/.
My code:
$('body').on('click', ':not(a)', function () {
// do stuff
});
I'd expect all click to on <a> to be ignored, but this is not the case.
Am I doing something wrong or is this a bug on jQuery's side?
There's a lot going on in that code that's not obvious. Most importantly, the click event is actually attached to the body element. Since that element isn't an anchor, you'll always get the alert. (Event delegation works because the click event bubbles up from the a through all its ancestors, including body, until it reaches document.)
What you want to do is check the event.target. That will tell you the element that was actually clicked on, but the actual click event is still bound to the body element:
$('body').on('click', function (e) { // e = event object
if ($(e.target).is(':not(a)')) {
alert('got a click');
}
});
http://jsfiddle.net/y3kx19z7/
No this is not a bug but rather intended behaviour.
The event bubbles all the way up. By clicking the a node, you are still triggering it's parents event from the div node.
Read more about event bubbling in the W3C DOM Specification. Just search for "bubble".
You need to stop the event propagation of the a nodes. i.e.:
$('body').on('click', ':not(a)', function () {
// do something effectively
alert('you should not see me when clicking a link');
});
$("a").click(function( event ) {
// do nothing effectively, but stop event bubbling
event.stopPropagation();
});
JSFiddle: http://jsfiddle.net/nhe6wk77/6/
It's working as intended, here's why!
Use of the :not() selector is honored in delegated events, but it's an uncommon practice because of how events bubble up the DOM tree potentially triggering the handler multiple times along the way.
The jQuery API Documentation states that:
jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
Notice the phrase "and runs the handler for any elements along that path matching the selector".
In your example, jQuery is accurately not running the handler on the a element, but as the event bubbles up the tree, it runs the handler for any element that matches :not(a), which is every other element in the path.
Here is a clear example showing how this works: http://jsfiddle.net/gfullam/5mug7p2m/
$('body').on('click', ':not(a)', function (e) {
alert($(this).text());
});
<div class="outer">
<div class="inner">
Click once, trigger twice
</div>
</div>
<div class="outer">
<div class="inner">
<button type="button">Click once, trigger thrice</button>
</div>
</div>
Clicking on the link in the first block of nested divs, will start the event bubbling, but the clicked a element — a.k.a. the event target — doesn't trigger the handler because it doesn't match the :not(a) selector.
But as the event bubbles up through the DOM, each of its parents — a.k.a the event currentTarget — triggers the handler because they do match the :not(a) selector, causing the handler to run twice. Multiple triggering is something to be aware of since it may not be a desired result.
Likewise, clicking on the button in the second block of nested divs, will start the event bubbling, but this time the event target does match the :not(a) selector, so it triggers the handler immediately. Then as the event bubbles up, each of its parents matching the selector triggers the handler, too, causing the handler to run three times.
As others have suggested, you need to either bind an alternate handler that stops propagation on a click events or check the event target against the :not(a) selector inside your handler instead of the delegated selector.
$("body").click(function(e) {
if($(e.target).is('a')){
e.preventDefault();
return;
}
alert("woohoo!");
});
check the target of the click. this way you dont need to bind another event.
updated fiddle
I have HTML similar to the following in my page
<div id="someDiv">
<img src="foo.gif" class="someImg" />
</div>
The wrapper div is set up such that when it is clicked, it's background-color changes using the following jQuery code.
$("div").click(function(event){
$(this).css("background-color", "blue");
});
I also have some jQuery associated with my img that will do some other function (for the sake of argument I am going to display and alert box) like so:
$("img[class=someImg]").click(function(event){
alert("Image clicked");
});
The issue I have come across is that when I click on the img, the event associated with the div is also triggered. I'm pretty sure that this is due to the way that jQuery (or indeed JavaScript) is handling the two DOM elements - clicking the img would require you to also technically click the div, thus triggering both events.
Two questions then really:
Is my understanding of the
DOM/JavaScript flawed in some way or
is this actually how things are
occurring?
Are there any jQuery methods that
would allow me to perform actions on
a child element without invoking
those associated with its parent?
That is known as event bubbling, you can prevent it with stopPropagation():
$("img[class=someImg]").click(function(event){
alert("Image clicked");
event.stopPropagation();
});
.
Is my understanding of the DOM/JavaScript flawed in some way or
is this actually how things are
occurring?
That is because of what is known event bubbling.
Are there any jQuery methods that would allow me to perform actions
on a child element without invoking
those associated with its parent?
Yes, you need stopPropagation()
No, this is by design. Events bubble up through the entire dom, if you put another handler on body, it would fire too
Yes :) JQuery normalizes the event object, so adding event.stopPropagation() in your img click handler will give you the behavior you expect on all browsers
The problem you just facing is called "event bubbling". That means, if you click on a nested
element, that click event will "bubble up" the DOM tree.
If other elements also are bound to an click event, their listeners will fire aswell.
Solution to prevent this is called:
stopPropagation()
which is used within your event handler
$("img[class=someImg]").click(function(event){
event.stopPropagation();
alert("Image clicked");
});
This is what's called event bubbling, and you can stop it to get the behavior you want with .stopPropagation() (or return false; if you want to stop the event completely, including handlers on the same level), like this:
$("img[class=someImg]").click(function(event){
alert("Image clicked");
event.stopPropagation();
});
You can view a demo here, comment it out and click run again to see the difference.
The short version is that when most event types happen, they happen on the immediate element, then bubble up the DOM, occurring on each parent as they go. This is not jQuery specific at all, native JavaScript does this. If you're more curious, I'd read the linked article, it has a great explanation of what's going on.
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.