I have a div and within the div there is a child img element.
Both div and img have their very own javascript click handler.
My objective is to have when I click on the img, only the img click is triggered.
But currently the div click handler is responding before the img click handler.
Here's the example: http://jsfiddle.net/5j73w/
I'm not sure if parent's position: relative is the culprit. But that's a compulsory style.
I also tried with z-index for img but no avail.
Thanks in advance!
Replace the deprecated .live() by .on() as you're using jQuery 1.7+.
$('#parent > img').on('click', function(e) {
Fiddle
Or, if you need the event delegation (e.g. in case you're adding content dynamically to #parent):
//run this line when #parent is in the DOM
$('#parent').on('click', '> img', function(e) {
Fiddle
.live bubbles the event all the way up to the document to then check if the given selector matches the target element, by then you can't stop the event propagation anymore. From the docs:
Calling event.stopPropagation() in the event handler is ineffective in stopping event handlers attached lower in the document; the event has already propagated to document.
Also, to answer the "parent takes precedence" question, that's not case. When you call .live, you're actually attaching a handler to the document.
In this case, the handler attached through .click(function(){}) (which in jQuery 1.7+ is a shorthand for .on('click'[, null], function(){}), executes before the handler attached to the document, which is the expected event propagation bubbling behavior.
Was able to fix by doing the following:
$('#parent').click(function(e)
{
console.log("parent click");
});
$('#child').click(function(e)
{
console.log("child click");
e.stopPropagation();
});
Here's the Fiddle.
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 know that for dynamic dom elements I need to use jQuery.fn.on or delegate, but if I 'move' the elements container appending to another elements in the dom, the click doesn't work anymore.
Here is the the jsFiddle to reproduce the issue:
http://jsfiddle.net/j0L7c51f/
Currently I'm using the following bind method:
$('#commoditySelector').on( 'click', 'li.available', function() {
var cmID = $(this).attr('data-cmid');
$('#debug').html('commoditySelected: '+ cmID);
});
If I comment out the code line where I move the ul element using appendTo(), the click event bound works fine.
The issue is caused by your use of mousemove, not the delegated event handler, as the HTML is being re-built every single time the mouse moves. This means that the delegated event handler is correctly fired on a clicked element, but that element is immediately removed from the DOM, so the event is cancelled before it propagates up the DOM to be processed.
To fix this issue, use the mouseenter event on the a instead:
$('#commodityCategories li a').mouseenter(function(e) {
// your code...
});
Updated fiddle
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 been experimenting with capturing click events outside of elements using stopPropagation().
$(".container").children().on('click',function(e){
e.stopPropagation();
});
$(".container").on("click",function(){
alert("outside the box?");
})
Here is a jsFiddle set up to demonstrate it functioning. An alert should fire when you click anywhere outside of the white box.
Now, I am trying to have the same principle applied to dynamically created elements. As far as I understand, the on() method of event assignment in jQuery should allow this to function without changing the script.
Here is a second jsFiddle where you must first click a link to create the elements. Once you have done this, the theory is that the same script will work, but it does not. What am I missing about this method?
When the item is added dynamically, you should attach the handler to the closest parent that will surely be there - in your case this is body. You can use on() this way to achieve a functionality that delegate() used to offer:
$(selector-for-parent).on(events, selector-for-dynamic-children, handler);
So your code rewritten would simply be this:
$("body").on('click', '.container', function(e){
var $target = $(e.target);
if ($target.hasClass('container')) {
alert("outside the box!");
}
});
I used e.target to find which element actually triggered the event. In this case, I identify the item by checking whether it has the container class.
jsFiddle Demo
In short word you need to put on() on existing parent element to make it works:
$('body').on('click', 'a', function(e){
e.preventDefault();
$('<div class="container"><div class="box"></div></div>').appendTo('body');
$(this).remove();
});
$('body').on('click', '.container > *', function(e){
e.stopPropagation();
});
$('body').on('click', '.container', function(){
alert("outside the box?");
})
Code: http://jsfiddle.net/GsLtN/5/
For more detail check '.on()' on official site at section 'Direct and delegated events'
The demo.
When you bind a event handler to a element use .on, the target you bind to must exist in the domcument.
$('body').on('click', '.container > *', function(e){
e.stopPropagation();
});
$('body').on("click",'.container',function(){
alert("outside the box?");
})
You need to bind the .on() to a parent.
What you're trying to do is - bind the handler to a parent that listens for an event, then checks whether the event was triggered by an element that matches that selector.
$("body").on("click", '.container',function(){
alert("outside the box?");
})
Updated fiddle here
According to the documentation for jQuery.on():
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
You will have to bind the event to a parent container. Perhaps something like THIS.
I would like to fire an event when anything on the page is clicked, and then process normally. For example a click would be fired, I would see if the target matched something, alert if it did, and then have the click event continue (no preventDefault()).
$(document).click(function(e) {
// e.target is the element which has been clicked.
});
This will handle all click events unless a handler prevents the event from bubbling up (by calling the stopPropagation() method of the event object).
$("body").click(function (event) {
// Your stuff here
}
3 options for you:
This is how .live() in jquery works. Everything bubbles to the top, and it matches the selector you set.
http://api.jquery.com/live/
A more efficient way to do it is using .delegate, or providing a context to .live() so you don't have to bubble to the top.
http://api.jquery.com/delegate/
If you want to do it manually, bind 'click' to the document, and use .closest() to find the closest matching selector:
http://api.jquery.com/closest/
It's all the same concept, event delegation as mentioned already.