So I'm trying to use the .hover() method that only does something if the element being hovered over, DOES NOT have a particular class. It works fine if I add the class to the element in question in HTML. But say I have a click event that uses .addClass() to add the class using jQuery. The hover() method then ignores the class that's been added. Enough rambling. Here is the code.
$('.foo:not(.bar)').hover(function() {
someCrap(); //when hovering a over .foo that doesn't also have the class .bar, do someCrap
}
$('.foo').click(function(){
$(this).addClass('bar'); //after the element .foo gets another class .bar, the hover event written earlier continues to work, WTF
})
any idea guys and girls?
.hover() only adds events to elements that is already created. You need to use .on() for also creating events for future elements. This means that you need to set an parent also. Preferably the parent should be as close as possible to the children that should have the mouse events, to save performance.
$('#parent').on({
mouseenter : function() {
"use strict";
// Do something
},
mouseleave: function() {
"use strict";
// Do something
}
}, ".foo:not(.bar)");
When you run $(selector).hover(fn); any elements that match selector will have the event bound to them. If you were to add/change/remove something about the selector that later on makes that element no longer match, the event would not care because it was already bound.
There are a couple of ways you can handle this, and it really depends on the volume of JavaScript in your application and the specifics of your use case.
1) You could use event delegation, which takes advantage of the way that JavaScript events bubble to allow you to bind an event to a parent element of a particular part of your page and do logic at the time in which the event happens - this is very useful for a couple of reasons. One, imagine you have a table with 100 records and a link with a class of edit that should fire a particular JavaScript function when someone clicks on it. If you do $('a.edit').click(fn); jQuery will end up binding 100 different events to the individual elements. This can start to make your application struggle in older machines/browsers. Secondly, it makes it so that when things change dynamically (either you added a new row to the table or removed the edit class from a link because you no longer want to allow a particular row to be editable) your event is smart enough to know what is going on and still work. In jQuery, you use the .on() function for this. Read up on the documentation. If you have earlier versions of jQuery you might have to use .delegate() and if you are running some really old jQuery functions you are looking for .live().
2) A simpler approach if your application is relatively small is to simply bind the event to all .foo elements and check within the event itself if the element currently has a class of bar and proceed if it doesn't.
I'd say it would be more like:
$( '.foo' ).hover(
function() { if ( $( this ).has( '.bar' ) ) {} else { // do something } },
function() { if ( $( this ).has( '.bar' ) ) {} else { // do something } }
);
Related
I have an input type="image". This acts like the cell notes in Microsoft Excel. If someone enters a number into the text box that this input-image is paired with, I setup an event handler for the input-image. Then when the user clicks the image, they get a little popup to add some notes to the data.
My problem is that when a user enters a zero into the text box, I need to disable the input-image's event handler. I have tried the following, but to no avail.
$('#myimage').click(function { return false; });
jQuery ≥ 1.7
With jQuery 1.7 onward the event API has been updated, .bind()/.unbind() are still available for backwards compatibility, but the preferred method is using the on()/off() functions. The below would now be,
$('#myimage').click(function() { return false; }); // Adds another click event
$('#myimage').off('click');
$('#myimage').on('click.mynamespace', function() { /* Do stuff */ });
$('#myimage').off('click.mynamespace');
jQuery < 1.7
In your example code you are simply adding another click event to the image, not overriding the previous one:
$('#myimage').click(function() { return false; }); // Adds another click event
Both click events will then get fired.
As people have said you can use unbind to remove all click events:
$('#myimage').unbind('click');
If you want to add a single event and then remove it (without removing any others that might have been added) then you can use event namespacing:
$('#myimage').bind('click.mynamespace', function() { /* Do stuff */ });
and to remove just your event:
$('#myimage').unbind('click.mynamespace');
This wasn't available when this question was answered, but you can also use the live() method to enable/disable events.
$('#myimage:not(.disabled)').live('click', myclickevent);
$('#mydisablebutton').click( function () { $('#myimage').addClass('disabled'); });
What will happen with this code is that when you click #mydisablebutton, it will add the class disabled to the #myimage element. This will make it so that the selector no longer matches the element and the event will not be fired until the 'disabled' class is removed making the .live() selector valid again.
This has other benefits by adding styling based on that class as well.
This can be done by using the unbind function.
$('#myimage').unbind('click');
You can add multiple event handlers to the same object and event in jquery. This means adding a new one doesn't replace the old ones.
There are several strategies for changing event handlers, such as event namespaces. There are some pages about this in the online docs.
Look at this question (that's how I learned of unbind). There is some useful description of these strategies in the answers.
How to read bound hover callback functions in jquery
If you want to respond to an event just one time, the following syntax should be really helpful:
$('.myLink').bind('click', function() {
//do some things
$(this).unbind('click', arguments.callee); //unbind *just this handler*
});
Using arguments.callee, we can ensure that the one specific anonymous-function handler is removed, and thus, have a single time handler for a given event. Hope this helps others.
maybe the unbind method will work for you
$("#myimage").unbind("click");
I had to set the event to null using the prop and the attr. I couldn't do it with one or the other. I also could not get .unbind to work. I am working on a TD element.
.prop("onclick", null).attr("onclick", null)
If event is attached this way, and the target is to be unattached:
$('#container').on('click','span',function(eo){
alert(1);
$(this).off(); //seams easy, but does not work
$('#container').off('click','span'); //clears click event for every span
$(this).on("click",function(){return false;}); //this works.
});
You may be adding the onclick handler as inline markup:
<input id="addreport" type="button" value="Add New Report" onclick="openAdd()" />
If so, the jquery .off() or .unbind() won't work. You need to add the original event handler in jquery as well:
$("#addreport").on("click", "", function (e) {
openAdd();
});
Then the jquery has a reference to the event handler and can remove it:
$("#addreport").off("click")
VoidKing mentions this a little more obliquely in a comment above.
If you use $(document).on() to add a listener to a dynamically created element then you may have to use the following to remove it:
// add the listener
$(document).on('click','.element',function(){
// stuff
});
// remove the listener
$(document).off("click", ".element");
To remove ALL event-handlers, this is what worked for me:
To remove all event handlers mean to have the plain HTML structure without all the event handlers attached to the element and its child nodes. To do this, jQuery's clone() helped.
var original, clone;
// element with id my-div and its child nodes have some event-handlers
original = $('#my-div');
clone = original.clone();
//
original.replaceWith(clone);
With this, we'll have the clone in place of the original with no event-handlers on it.
Good Luck...
Updated for 2014
Using the latest version of jQuery, you're now able to unbind all events on a namespace by simply doing $( "#foo" ).off( ".myNamespace" );
Best way to remove inline onclick event is $(element).prop('onclick', null);
Thanks for the information. very helpful i used it for locking page interaction while in edit mode by another user. I used it in conjunction with ajaxComplete. Not necesarily the same behavior but somewhat similar.
function userPageLock(){
$("body").bind("ajaxComplete.lockpage", function(){
$("body").unbind("ajaxComplete.lockpage");
executePageLock();
});
};
function executePageLock(){
//do something
}
In case .on() method was previously used with particular selector, like in the following example:
$('body').on('click', '.dynamicTarget', function () {
// Code goes here
});
Both unbind() and .off() methods are not going to work.
However, .undelegate() method could be used to completely remove handler from the event for all elements which match the current selector:
$("body").undelegate(".dynamicTarget", "click")
I know this comes in late, but why not use plain JS to remove the event?
var myElement = document.getElementById("your_ID");
myElement.onclick = null;
or, if you use a named function as an event handler:
function eh(event){...}
var myElement = document.getElementById("your_ID");
myElement.addEventListener("click",eh); // add event handler
myElement.removeEventListener("click",eh); //remove it
This also works fine .Simple and easy.see http://jsfiddle.net/uZc8w/570/
$('#myimage').removeAttr("click");
if you set the onclick via html you need to removeAttr ($(this).removeAttr('onclick'))
if you set it via jquery (as the after the first click in my examples above) then you need to unbind($(this).unbind('click'))
All the approaches described did not work for me because I was adding the click event with on() to the document where the element was created at run-time:
$(document).on("click", ".button", function() {
doSomething();
});
My workaround:
As I could not unbind the ".button" class I just assigned another class to the button that had the same CSS styles. By doing so the live/on-event-handler ignored the click finally:
// prevent another click on the button by assigning another class
$(".button").attr("class","buttonOff");
Hope that helps.
Hope my below code explains all.
HTML:
(function($){
$("#btn_add").on("click",function(){
$("#btn_click").on("click",added_handler);
alert("Added new handler to button 1");
});
$("#btn_remove").on("click",function(){
$("#btn_click").off("click",added_handler);
alert("Removed new handler to button 1");
});
function fixed_handler(){
alert("Fixed handler");
}
function added_handler(){
alert("new handler");
}
$("#btn_click").on("click",fixed_handler);
$("#btn_fixed").on("click",fixed_handler);
})(jQuery);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="btn_click">Button 1</button>
<button id="btn_add">Add Handler</button>
<button id="btn_remove">Remove Handler</button>
<button id="btn_fixed">Fixed Handler</button>
I had an interesting case relevant to this come up at work today where there was a scroll event handler for $(window).
// TO ELIMINATE THE RE-SELECTION AND
// RE-CREATION OF THE SAME OBJECT REDUNDANTLY IN THE FOLLOWING SNIPPETS
let $window = $(window);
$window.on('scroll', function() { .... });
But, to revoke that event handler, we can't just use
$window.off('scroll');
because there are likely other scroll event handlers on this very common target, and I'm not interested in hosing that other functionality (known or unknown) by turning off all of the scroll handlers.
My solution was to first abstract the handler functionality into a named function, and use that in the event listener setup.
function handleScrollingForXYZ() { ...... }
$window.on('scroll', handleScrollingForXYZ);
And then, conditionally, when we need to revoke that, I did this:
$window.off('scroll', $window, handleScrollingForXYZ);
The janky part is the 2nd parameter, which is redundantly selecting the original selector. But, the jquery documentation for .off() only provides one method signature for specifying the handler to remove, which requires this middle parameter to be
A selector which should match the one originally passed to .on() when attaching event handlers.
I haven't ventured to test it out with a null or '' as the 2nd parameter, but perhaps the redundant $window isn't necessary.
I am working on a mobile app, and I am currently using the on() method to implement a swipe-to-delete feature (I understand that there are libraries that would allow me to do this, and am open to any thoughts you have on the merits of different options). I have code that looks like this:
var favArticles = $('#favoritesList li');
favArticles.each(function(i, li){
var id = $(li).attr('id');
$(li).on("swipeLeft",function(){
//console.log('SwipeLeft ' + id);
var html = $(li).html();
var button = '<div ><button onclick="favDelete(id, i)">Delete</a></div>';
$(li).html('<div style="position:relative;">' + html + button + '</div>');
});
});
I am trying to manage a mutable list of articles that, so whenever I render the favorites list, I grab all the current articles, and bind a swipe event to them. If swiped, a button is brought up on top of the article, and when the user hits the button a function runs that removes the swiped li from the list, and deletes it from the stored favorites.
Within favDelete, I use the index i to remove() the correct li. This means that I need to recreate all the events with updated values of i each time an element is deleted.
So, my question: if I call on() again, for the same event on the same DOM element, will the old binding be overwritten? Or do I create a memory leak by constantly adding new on() actions to my list elements?
UPDATE: Yes, JQuery, not Javascript. Apologies. And I know that my favDelete call won't work as it is shown, I omitted chopped a bunch of quotation marks out for the post to try to improve readability.
I would just use two event handlers and event delegation: One for the swipe event and one for the click on the delete button.
I don't know if swipeLeft works with event delegation, but even if not, it would not change much:
$('#favoritesList').on('swipeLeft', 'li', function() {
// show delete button
// or $(this).html(...)
$(this).append('<div class="deleteButton"><button>Delete</a></div>');
}).on('click', '.deleteButton button', function() {
// find ancestor li element
var $li = $(this).closest('li');
// and pass it to favDelete
favDelete($li);
// if you don't remove the element in the favDelete, do it here:
$li.remove();
});
Using event delegation for the delete buttons makes the most sense, since you are "constantly" adding and removing them.
Learn more about event delegation.
All the styling you can do with a CSS rule for the deleteButton class. You'd also have to change your favDelete method to accept a li element (or rather a jQuery object with a li element) instead of an ID and index.
if I call on() again, for the same event on the same DOM element, will the old binding be overwritten?
.on() will always add a new event handler. In your code, you even create a new event handler function for every list element, which is indeed a waste of memory.
In my code above, there are only two event handlers for all li and button elements.
Two other ways you could do this, rather than a hard-coded index:
Use the ID to select the correct LI element (in your favDelete function)
Pass in a selector instead of the index (i.e. $(this).closest('li'))
If You'll call 'on' again, previous bindings will still be alive - for ex. If You'll call few times (let's say 5 times) on('click', function() { console.log('fired'); }. When You'll click one time on the object it'll thorw 'fired' five times.
I hope I'm making the question clear.
Say you have some check-boxes (or any other type of element for that matter) for which you register events handler when the page loads. Then you add some more check-boxes using AJAX (so no page reload). But you also want these newly added check-boxes (after the page was loaded) to have the same registered events handlers?
What I tried was this but I feel there has to be a better approach:
$(document).ready(function () {
// Register custom envets handler
registerCustomEventHandlers();
$('a').on('click', addExtraFields);
});
function registerCustomEventHandlers() {
$('input.class_name').on("change", sayHelloWorld);
}
function sayHelloWorld() {
alert('Hello world');
}
function addExtraFields() {
// insert some checkboxes...
// register the events handler again for the newly added fields
registerCustomEventHandlers();
}
So basically inside the function that adds the check-boxes I register again all the events handlers. I was looking at something like this $(document).change(function() {}); but apperently it is not supported by all browsers...
Any suggestions?
You can delegate the event to the document instead so that they will be applied to all future inputs as well. You don’t even need to put it in a domReady event, since the document is always available:
$(document).on("change", "input.class_name", sayHelloWorld);
function sayHelloWorld() {
alert('Hello world');
}
function addExtraFields() {
// insert some checkboxes, they will have the same handler attached when inserted
}
Demo: http://jsfiddle.net/gdX3R/1/
I would recommend against using live selectors because of these reasons
Shortly summed up, it's a performance issue because it messes with every click event.
Instead, just use delegate like described in the post on lowest common parent element of the inputs (most likely a form):
$('#yourFormId').delegate('.class_name', 'click', function() {
// what you want to do
});
You can find a jsfiddle here
And don't use selectors like input.class_name (unless there elements other than input with that class name). They're slower than .class_name because they're looping through all the inputs in the form searching for elements with that class rather than just selecting by class.
As of jquery 1.4 you can always use live() too http://api.jquery.com/live/ which allows you to attach handlers now and in the future to any matching elements.
the way this is done in Jquery is such that you don't need the object to be present when creating the handler.
You could use:
$(document.body).on("click", "input[type='checkbox']",
function(){alert($(this))}
);
This will be applied to any new check-box added to the page, no matter timing.
With JQuery, is it possible to add an event listener to any element that currently, or will in the future, have a particular class?
I'm working on a project that makes heavy use of contentEditable, so the DOM is changing, and elements can have classes added and removed as a result of user input.
I would like to be able to say "elements of class X should do Y when clicked", but if I understand correctly, $(".X").click(Y) will only add the event listener to elements that currently have class X.
Furthermore, if an element is no-longer part of class X, then it will still have the click event listener.
How can I do this?
Yep. What you're talking about is called event delegation. Here's an example:
$('#container').on('click', '.innerElement', function(){
/// Do stuff
});
In your case, #container would be an element that is known to exist on page load which will contain the child elements you care about (either now or in the future). This approach takes advantage of event bubbling in the DOM.
As another poster mentioned, the live method will also work -- but it has been deprecated in jQuery 1.7, and is generally not as performant as using more selective delegation (such as the example above).
you'll want to use event delegation. jquery 1.7 has made this more abstract than previous versions, but it looks something like this:
$("#myWrappingElement").on("click", ".myclass", function(event){
alert($(this).text());
});
this basically adds a click event listener to the #myWrappingElement element, and jquery will automagically look to see what the original event target was and fire the proper function. this means you can add or remove .myclass elements and still have events fire on them.
the jQuery live() method swill allow to have a "live" action listener - so if new DOM elements match the selector, they will be attached to the action listener. For example:
$(".X").live("click", function(){
alert('some action');
});
See the documentation here for more info: http://api.jquery.com/live/
I'm not sure that the second part of your question about keeping the action listener attached after removing the class os possible - someone else might have a solution though.
I am using Jquery to dynamically add some HTML into a page.
Now this new HTML code should trigger additional Jquery functions to enable more processing to be done but this new HTML code isnt recognized and thus the additional Jquery functions arent triggered.
How can I get the new HTMl code to be recognized and the additional functions triggered?
Thanx
It depends on what you want to do. The first thing to look into would be jQuery's .live() methods. You can associate events to matching elements that either exist or will exist in the future. For example, this click method will only bind to existing elements with the class of 'clickme'
$('.clickme').bind('click', function() {
// Bound handler called.
});
However, if you bind it using the.live() methods then it will work for existing elements and any new elements that are created:
$('.clickme').live('click', function() {
// Live handler called.
});
These examples are taken right off the API page for the live method. Check it out here: http://api.jquery.com/live/
There are 2 concerns normally, event handlers and plugins, which are two different things.
Part 1: Event Handlers
Event handlers are easy, because they act upon events, events behave identically no matter when the element as added. For this there's .live() and .delegate(), .live() listens for events on document and runs if an event comes from an element that matches the selector, let's take a table row for example:
$("tr").click(function() { ... });
This would find all current table rows, when it ran and bind a click event handler to them, the same as .bind('click', function). Then there's .live(), like this:
$("tr").live('click', function() { ... });
This listens for the click event to bubble up to document (this happens automatically, by default) and executes the handler...current and future elements behave the same way here. This means it works for both. Then there's .delegate() which is a local version of .live() like this:
$("#myTable").delegate('tr', 'click', function() { ... });
If you're just adding rows to #myTable but not removing/adding the table itself, the same type of listener for bubbling events can sit there, instead of all the way up on document, this means the event has to bubble fewer times before reaching the handler you want to execute.
Part 2: Plugins
Plugins are a bit trickier, because they take elements and do things with them (this is true for most plugins). You have two decent options here, either running the plugin when new elements yourself, for example loading via $.ajax() or a shorthand version would look like this:
$.ajax({
//options...
success: function(data) {
//add elements
$("tr", data).myPlugin();
}
});
This finds new <tr> elements, but only in a local context (in the returned HTML) and executes only on those elements. Alternatively, there's a plugin for this, less efficient, but usually not a noticeable difference on most pages. The .livequery() plugin actively looks for and acts up new elements, the same code would look like this:
$("tr").livequery(function() {
$(this).myPlugin();
});
Either of these are valid solutions, just see which fits your needs better.
More details might be helpful but it sounds like Jquery.live() might be what you need. Jquery.live() binds handlers to elements dynamically.