can't remove specific event handlers when attached to document with .on() - event-handling

Here's a simple fiddle to demo my situation...
http://jsfiddle.net/UnsungHero97/EM6mR/17/
What I'm doing is adding an event handler for current & future elements, using .on(). I want to be able to remove these event handlers for specific elements when something happens; in the case of the fiddle, when the radio button is selected, the event handler for the blue elements should be removed and clicking those elements should not do anything anymore.
It doesn't seem to be working :(
How do I remove the event handler attached to document that I created with .on() for those specific blue elements?

The signature for your .on() and .off() has to match.
These two do not match so the .off() call won't find matching event handlers to remove:
$(document).on('click', '.btn', function() {
update();
});
$(document).off('click', '.blue');
Note, the selector passed to .on() and .off() is different.
When using the dynamic form of .on() (where you pass a selector as an argument to .on()), you can't remove just part of the items. That's because there's only one event handler installed on the root element and jQuery can only remove the entire thing or not at all. So, you can't just .off() some of the dynamic items.
Your options are to remove all the event handlers with:
$(document).off('click', '.btn');
and, then install a new event handler that excludes the items you don't want such as:
$(document).off('click', '.btn:not(.blue)');
Or, teach the event handler itself how to ignore .blue items:
$(document).on('click', '.btn', function() {
if (!$(this).hasClass('blue')) {
update();
}
});

Be careful of how you attach your events; this works fine for me:
$('.btn').on('click', function() {
update();
});
$('#disable').on('change', function() {
$('.btn').off('click');
});

Only way seems to be:
$('#disable').on('change', function() {
$(document)
.off('click', '.btn')
.on('click', '.btn:not(.blue)', update);
});

Related

Unbinding event handler that was bound with .on()

I'm trying to unbind some event handlers that were bound with .on(), but nothing seems to work:
HTML:
<div class='parent'>
<a id='test-1' class='test'>Test 1</a>
<a id='test-2' class='test'>Test 2</a>
<a id='test-3' class='test'>Test 3</a>
</div>
<hr>
<a class='unbind'>Unbind Test 1</a>
JS:
$('.parent').on('click', 'a.test', function(e){
alert('click');
});
$('a.unbind').click(function(e){
$('a#test-1').unbind('click');
$('a#test-1').off('click');
});
Fiddle:
http://jsfiddle.net/g9ucd/5/
CLARIFICATION: Per the example, I want to know if it's possible to unbind specific elements that had previously been bound with .on(), rather than just reversing the all the bindings with .off().
The jQuery API on method documentation states that the event is actually placed on the elemented it is called from, the filter selector you apply is used to then determine which elements the event is valid on. So the short answer is NO you can't unbind the even from an element that is designated by the filter selector.
However, have you considered working around this by constraining your selector further?
You could remove the test class from the a element and then by that scenario it would no longer meet the filter selector's criteria and thus be untied to the event.
Just a thought.
Here's my fiddle and below is the snipped that i changed.
$('a.unbind').click(function(e){
$('a#test-1').removeClass('test');
});
How about this:
var clickie = function(e){
alert('click');
}
$('.parent').on('click', 'a.test', clickie);
$('a.unbind').click(function(e){
$('.parent').off('click', 'a.test', clickie);
});
Thing is, you never set a handler on a#test-1 - you set it on .parent. You can't remove what you didn't set. If you need to remove a handler from a#test-1, you must not use the live functionality: $('.parent a.test').on('click', clickie) will bind your function onto the elements themselves, so you can off them individually.
You can try this
$('a.test').on('click', function(e){
alert('click');
});
$('a.unbind').click(function(e){
$('a#test-1').unbind('click');
$('a#test-1').off('click');
});
You need to add more logic to it:
$('.parent').on('click', 'a.test', function(e){
// retrieve switch value:
var disable_calls = $(this).data('disable-calls') || false;
if (!disable_calls){
// your logic here...
alert('click!');
};
});
$('a.unbind').click(function(e){
// disable call by turning the switch:
$('a#test-1').data('disable-calls', true);
});
Event handlers are attached to the outer element (.parent) and jQuery gives you a shortcut for actually checking event.target (you do that by supplying selector within .on() call). To alter that logic, you need to add your own special handling, or eg. make sure that selector is no longer matched:
$('.parent').on('click', 'a.test.calls-enabled', function(e){
alert('click!');
});
$('a.unbind').click(function(e){
// disable call by turning the switch:
$('a#test-1').removeClass('calls-enabled');
});
A variation on #ermagana's and #Tadeck's answers is simply to use the :not selector on the initial delegated binding. That way, you can "unbind" certain elements by explicitly disabling them with an extra class.
(In this case, I'll use the .disable class, which would give extra benefit of disabling them visually if you were using Bootstrap etc..)
$('.parent').on('click', 'a.test:not(.disabled)', function(e){
alert('click');
});
$('a.unbind').click(function(e){
$('a#test-1').addClass('disabled');
});

Ignore multiple button taps after first one on iPhone webapp using jQuery Mobile?

Assume button A in an HTML5 webapp built with jQuery Mobile.
If someone taps button A, we call foo(). Foo() should get called once even if the user double taps button A.
We tried using event.preventDefault(), but that didn't stop the second tap from invoking foo(). event.stopImmediatePropagation() might work, but it also stops other methods further up the stack and may not lead to clean code maintenance.
Other suggestions? Maintaining a tracking variable seems like an awfully ugly solution and is undesirable.
You can set a flag and check if it's OK to run the foo() function or unbind the event for the time you don't want the user to be able to use it and then re-bind the event handler after a delay (just a couple options).
Here's what I would do. I would use a timeout to exclude the subsequent events:
$(document).delegate('#my-page-id', 'pageinit', function () {
//setup a flag to determine if it's OK to run the event handler
var okFlag = true;
//bind event handler to the element in question for the `click` event
$('#my-button-id').bind('click', function () {
//check to see if the flag is set to `true`, do nothing if it's not
if (okFlag) {
//set the flag to `false` so the event handler will be disabled until the timeout resolves
okFlag = false;
//set a timeout to set the flag back to `true` which enables the event handler once again
//you can change the delay for the timeout to whatever you may need, note that units are in milliseconds
setTimeout(function () {
okFlag = true;
}, 300);
//and now, finally, run your original event handler
foo();
}
});
});
I've created a sample here http://jsfiddle.net/kiliman/kH924/
If you're using <a data-role="button"> type buttons, there is no 'disabled' status, but you can add the appropriate class to give it the disabled look.
In your event handler, check to see if the button has the ui-disabled class, and if so, you can return right away. If it doesn't, add the ui-disabled class, then call foo()
If you want to re-enable the button, simply remove the class.
$(function() {
$('#page').bind('pageinit', function(e, data) {
// initialize page
$('#dofoo').click(function() {
var $btn = $(this),
isDisabled = $btn.hasClass('ui-disabled');
if (isDisabled) {
e.preventDefault();
return;
}
$btn.addClass('ui-disabled');
foo();
});
});
function foo() {
alert('I did foo');
}
});

How to implement event order in parent/child elements using jquery

I have a parent & child elements in my web page where both have separate event handlers defined for them. I have tested in firefox, where the event handler for parent element gets executed first. I want it to execute the other way - child element's event handler getting executed first.
I have read about making use of bind, stopPropagation, preventDefault etc. make that happen but I am a bit confused as a to how to get this working? Can somebody shed some light on this topic?
Here is how I am implementing the event handling....
$('#Sidebar ul li .DeleteList').live('click', function(e) {
alert("I was deleted");
});
$('#Sidebar ul').delegate('li', 'click', function(e) {
alert("I was selected");
});
Why not be consistent and just use .live() for both? :D
$('#Sidebar ul li .DeleteList').live('click', function(e) {
alert("I was deleted");
});
$('#Sidebar ul li').live('click', function(e) {
alert("I was selected");
});
This way 'deleted' gets triggered first.
fiddle: http://jsfiddle.net/c9sBD/

jquery selection with .not()

I have some troubles with jQuery.
I have a set of Divs with .square classes. Only one of them is supposed to have an .active class. This .active class may be activated/de-activated onClick.
Here is my code :
jQuery().ready(function() {
$(".square").not(".active").click(function() {
//initialize
$('.square').removeClass('active');
//activation
$(this).addClass('active');
// some action here...
});
$('.square.active').click(function() {
$(this).removeClass('active');
});
});
My problem is that the first function si called, even if I click on an active .square, as if the selector was not working. In fact, this seems to be due to the addClass('active') line...
Would you have an idea how to fix this ?
Thanks
Just to give something different from the other answers. Lonesomeday is correct in saying the function is bound to whatever they are at the start. This doesn't change.
The following code uses the live method of jQuery to keep on top of things. Live will always handle whatever the selector is referencing so it continually updates if you change your class. You can also dynamically add new divs with the square class and they will automatically have the handler too.
$(".square:not(.active)").live('click', function() {
$('.square').removeClass('active');
$(this).addClass('active');
});
$('.square.active').live('click', function() {
$(this).removeClass('active');
});
Example working: http://jsfiddle.net/jonathon/mxY3Y/
Note: I'm not saying this is how I would do it (depends exactly on your requirement) but it is just another way to look at things.
This is because the function is bound to elements that don't have the active class when you create them. You should bind to all .square elements and take differing actions depending on whether the element has the class active:
$(document).ready(function(){
$('.square').click(function(){
var clicked = $(this);
if (clicked.hasClass('active')) {
clicked.removeClass('active');
} else {
$('.square').removeClass('active');
clicked.addClass('active');
}
});
});

jQuery live with the ready or load event

I'm using the jQuery Tools tooltip plugin, which is initialized with $('selector').tooltip(). I'd like to call this on any current or future .tooltipper element. I figured that the following would work:
$('.tooltipper').live('ready', function(){
$(this).tooltip()
}
But it was unsuccessful---the ready event did not fire. The same for load. I've read that livequery can produce the result of I'm looking for, but surely there is a way to use jQuery .live() to pull it off, considering the documentation says that it works for all jQuery events, of which I believe ready is one.
Quoted from the jQ API (http://api.jquery.com/live/):
In jQuery 1.3.x only the following JavaScript events (in addition to custom events) could be bound with .live(): click, dblclick, keydown, keypress, keyup, mousedown, mousemove, mouseout, mouseover, and mouseup.
As of jQuery 1.4 the .live() method supports custom events as well as all JavaScript events.
As of jQuery 1.4.1 even focus and blur work with live (mapping to the more appropriate, bubbling, events focusin and focusout).
As of jQuery 1.4.1 the hover event can be specified (mapping to "mouseenter mouseleave").
.live() does not appear to support the ready event.
To add to HurnsMobile's excellent answer; Looking at bindReady(), which is the internal call that jQuery makes to bind to the document load event every time you call $(some_function) or $(document).ready(some_function) we see why we cannot bind to "ready":
bindReady: function() {
if ( readyBound ) {
return;
}
readyBound = true;
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
return jQuery.ready();
}
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", DOMContentLoaded);
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
try {
toplevel = window.frameElement == null;
} catch(e) { //and silently drop any errors
}
// If the document supports the scroll check and we're not in a frame:
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
}
To sum it up, $(some_function) calls a function which binds to:
DOMContentLoaded
onreadystatechange (DOMContentLoaded)
window.load / onload
Your best bet would be to bind to those actions that might create new .tooltipper elements, rather than trying to listen for the ready event (which happens only once).
HurnsMobile is right. JQuery live does not support the ready-event.
This is why I created a plugin that combines the two. You register your callback once, and then you will need to call the plugin once for content you add manually.
$.liveReady('.tooltipper', function(){
this.tooltip()
});
Then when creating new content:
element.html(somehtml);
element.liveReady();
or
$('<div class="tooltipper">...').appendTo($('body')).liveReady();
A demo is available here: http://cdn.bitbucket.org/larscorneliussen/jquery.liveready/downloads/demo.html
Check out the introductory post here: http://startbigthinksmall.wordpress.com/2011/04/20/announcing-jquery-live-ready-1-0-release/
Also have a look at http://docs.jquery.com/Plugins/livequery, which listenes for changes on the dom.