I have a table where i have bound all my elements with class="shift" to a click function.
Now, because I also need to use another click event on part of the element, I would like to unbind the click event on element when the mouse enters the element and rebind when i leaves (meant for some touch events and whatnot)
Now, I bind like this
$("table").on("touchstart mousedown",".shift", function(e){ ... })
But when i try to unbind on a specific element, say it has a class="selected" added to distinguish the current element i use:
$("table").off("touchstart mousedown",".shift.selected")
which does not work....
I can remove all the handlers at once, but it would be wasteful to remove all the handlers and reinsert them as soon as the mouse leaves.
So, is there a way to remove the handler on a single element after the event is bound to all current and future elements?
Thanks in advance!
You don't need to unbind the click event on the element when the mouse enters. I know, the element click event will trigger when you click an inner element with the click event bound, right ? you can stop that:
The click handler of the inner element must look like this:
$("some inner element").click(function(event) {
//That's what are you looking for ;)
event.stopPropagation();
//You code here
});
event.stopPropagation() will prevent the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
Related
My website works basically with javascript. the server returns the html in a JSON array then javascript will add it to DOM.
My problem is with events.
Should I add the click event to body/window and then check if target match or add event to every element ? (the most events will be in the feed, where elements have same class):
<div id="feed_1">
<div class="like_button" data-click="something here">Like</div>
</div>
<div id="feed_2">
<div class="like_button" data-click="something here">Like</div>
</div>
note: I'm not using jQuery
If you are asking whether or not you should add an even to every element in the HTML document, or to have one event listener that specifies a target upon a click, the better way is definitely the latter.
With events that work with a large number of elements, we enforce DRY principles through the use of event delegation. This involves declaring an event listener with the event parameter, and then triggering the event onto the event target. example:
var el = document.getElementByID('container');
el.addEventListener('click', function(e) {
doSomething(e.target); // do something to the target element
});
The idea here is that your container element encompasses everything you wish to be 'clickable', however, when you click an element within the container, the event will only fire for that clicked element. This way we prevent having to define an event listener for each individual element.
Hope this helps.
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 need to attach a JavaScript click listener to an add new record confirmation on a DevExpress gridview.
I can't use a regular click event listener as it's loaded via AJAX integrated into the control. I also have no access to the button's code so am unable to extend it.The only thing I do have is the button name.
Ideally I want to listen for the appearance of the button on the DOM and then attach the listener, is there any way to do this?
You do not need to wait for the appearance of the button in the DOM.
Just use a delegated event handler attached to a non-changing ancestor of the dynamic elements.
e.g.
$(document).on('click', '.someclass', function(){
///
});
If you only have the element name for the button use an attribute selector:
e.g.
$(document).on('click', '[name="somename"]', function(){
///
});
Delegated events work by listening for events bubbling up to a non-changing ancestor (document is the default if nothing closer is available). It then applies the selector at event time (not at event registration time). It then calls the function for any matching element that caused the event.
The end result is that it will work with elements that may exist later, when the event occurs.
Note: if nothing is closer to the dynamic content, use document, but do not use 'body' as styling can make it have a zero height and delegated mouse events will not work!
Working with google maps which has infowindows that popup. These infowindows have images which are click-able. As a result I need to be able to propagate events in the infowindow. However I am also able to click THROUGH the infowindow to other markers which causes the current infowindow to close and it to open a new infowindow.
I dont think this issue is specific to google maps. Is there a way to stop events from propagating through an element?
Thought the below code would help but it didnt.
$(document).on('touchstart', '.infoWindow', function(e){
if ($(e.currentTarget) == $(this)) e.stopPropagation();
});
if ($(e.currentTarget) == $(this))
This is never true. It creates two distinct jQuery instances, which - even if they contain the same element - are not the same object. You might have wanted to do
if (e.currentTarget == this)
but this is always true - by definition of the event dispatch algorithm, the this value of an event listener and the currentTarget of the event object always refer to the same thing. Hell, even jQuery does this.
So you should just write
$(document).on('touchstart', '.infoWindow', function(e){
e.stopPropagation();
});
to prevent touchstart events from bubbling through your infoWindows.
You cannot stop the event propagation when you use event delegation. Event delegation relies on the bubbleing event, so by the time the delegated handler gets the event, the event already bubbled through every other element. You need to attach your event listener directly to the element.
$('.infoWindow').on('touchstart', function(e){
e.stopPropagation();
});
Event delegation works by adding the event listener to the document, then every time the document receives that touchstart event, jQuery checks the source element and all of its parent if they match the given selector. If one matches, the handler gets invoked. This is why you don't need to add the listener every time you add a new element that would match the given selector.
Setting a background color of the .infoWindow element might be a solution.
If it should be transparent, you can use as follows:
background-color: rgba(255, 0, 0, 0);
When I access <div> or <p> elements by class with jQuery for a click function, it repeats the event by how many elements are in the array or stack. So, if I have 3 <div> elements on top of each other or next to each other, the one on the bottom, or the one to the right, will go through the event once and the one on the top or the left will go through the event 3 times.
Am I doing something wrong? Or is this not meant to be done with jQuery?
[revision]
sorry if i worded this in a confusing way. here is a link... you will better understand my problem there. just add a couple new elements via the form and click on them.
http://jsfiddle.net/rNj6e/
Now that you've posted a fiddle showing the problem, I can actually answer. The problem is that you bind the click event handler to .dp inside the click event handler bound to #add. So what happens is this:
You fill in the form and click #add, which creates a new element with class dp and appends it
It then binds a click event handler to every element with class dp (there's only 1, the 1 we just added)
You fill in the form again, click #add, which repeats steps 1 and 2, so it binds another click event listener to the first .dp element, and binds one to the new element.
Repeat as necessary, binding more and more event handlers to the existing elements every time you click #add!
To fix this, you need to bind the event handler to .dp outside of the #add event handler. The problem is that you're creating new .dp elements on the fly, so just using .click won't bind to elements that are not in the DOM yet. To solve that, you can use delegate, which binds an event handler to elements matching the selector now and in the future:
$("#preview").delegate(".dp", "click", function(event){
alert(this.id);
});
Here's an updated fiddle.
Try:
$(".some_class").click(function(event){
alert(event.target.id);
}).children().click(function(event) {
return false;
});
This should prevent the click event from bubbling through the children, this happens when you click on the children contained in the div.
You are recieving a blank ID because the target of your clicks is most likely the child you clicked and you haven't given it an ID.
To prove this try...
$(".some_class").click(function(event){
alert(event.target.nodeName);
});
which alerts you the nodeName (the name of the html tag) you just clicked.
Change it to this:
$(".some_class").each( function () {
jQuery(this).click(function(event){
alert(event.target.id);
});
});