There are times when we will
be done with an event handler we previously registered. Perhaps the
state of the page has changed such that the action no longer makes
sense. It is typically possible to handle this situation with
conditional statements inside our event handlers, but it may be more
elegant to unbind the handler entirely.
Suppose that we want our
collapsible style switcher to remain expanded whenever the page is not
using the normal style. While the Narrow Column or Large Print button is selected, clicking the background of the style switcher should do nothing. We can accomplish this by calling the .unbind() method to remove the collapsing handler when one of the non-default style switcher buttons is clicked.
$(document).ready(function() {
$('#switcher').click(function(event) {
if (!$(event.target).is('.button')) {
$('#switcher .button').toggleClass('hidden');
}
});
$('#switcher-narrow, #switcher-large').click(function() {
$('#switcher').unbind('click');
});
});
Now when a button such as Narrow Column is clicked, the click handler on the style switcher <div>
is removed, and clicking the background of the box no longer collapses
it. However, the button doesn't work anymore! It is attached to the click event of the style switcher <div> as well because we rewrote the button-handling code to use event delegation. This means that when we call $('#switcher').unbind('click'), both behaviors are removed.
Event namespacing
We need to make our .unbind() call more specific, so that it does not remove both of the click handlers we have registered. One way of doing this is to use event namespacing.
We can introduce additional information when an event is bound that
allows us to identify that particular handler later. To use namespacing,
we need to return to the non-shorthand method of binding event
handlers, the .bind() method itself.
The first parameter we pass to .bind()
is the name of the JavaScript event we want to watch for. We can use a
special syntax here, though, that allows us to subcategorize the event.
$(document).ready(function() {
$('#switcher').bind('click.collapse', function(event) {
if (!$(event.target).is('.button')) {
$('#switcher .button').toggleClass('hidden');
}
});
$('#switcher-narrow, #switcher-large').click(function() {
$('#switcher').unbind('click.collapse');
});
});
The .collapse suffix is invisible to the event handling system; click events are handled by this function, just as if we wrote .bind('click'). However, the addition of the namespace means that we can unbind just this handler, without affecting the separate click handler we wrote for the buttons.
There are other ways of making our .unbind()
call more specific, as we will see in a moment. However, event
namespacing is a useful tool in our arsenal.
Rebinding events
Now clicking the Narrow Column or Large Print button causes the style switcher collapsing functionality to be disabled. However, we want the behavior to return when the Default button is pressed. To do this, we will need to rebind the handler whenever Default is clicked.
First, we should give our handler function a name so that we can use it more than once without repeating ourselves:
$(document).ready(function() {
var toggleStyleSwitcher = function(event) {
if (!$(event.target).is('.button')) {
$('#switcher .button').toggleClass('hidden');
}
};
$('#switcher').bind('click.collapse', toggleStyleSwitcher);
});
Note that we are here using a new syntax for defining a function. Rather than defining the function by leading with the function keyword, we assign an anonymous function to a local variable.
This is a stylistic choice to make our event handlers and other
function definitions resemble each other more closely; the two syntaxes
are functionally equivalent.
Also, recall that .bind() takes a function reference
as its second argument. It is important to remember, when using a named
function here, to omit parentheses after the function name; parentheses
would cause the function to be called, rather than referenced.
Now that the function has a name, we can bind it again later without repeating the function definition:
$(document).ready(function() {
var toggleStyleSwitcher = function(event) {
if (!$(event.target).is('.button')) {
$('#switcher .button').toggleClass('hidden');
}
};
$('#switcher').bind('click.collapse', toggleStyleSwitcher);
$('#switcher-narrow, #switcher-large').click(function() {
$('#switcher').unbind('click.collapse');
});
$('#switcher-default').click(function() {
$('#switcher')
.bind('click.collapse', toggleStyleSwitcher);
});
});
Now the toggle behavior is bound when the document is loaded, unbound when Narrow Column or Large Print is clicked, and rebound when Normal is clicked after that.
We have sidestepped a
potential pitfall here. Remember that when a handler is bound to an
event in jQuery, previous handlers remain in effect. This would seem to
mean that if Normal was clicked multiple times in succession, many copies of the toggleStyleSwitcher handler would be bound, causing strange behavior when the <div>
was clicked. Indeed, if we had used anonymous functions throughout our
example, this would be the case. But since we gave the function a name
and used the same function throughout the code, the behavior is only
bound once. The .bind() method will not attach an event handler to an element if it has already been attached.
As another benefit to naming this function, we no longer need to use namespacing. The .unbind() method can take a function as a second argument; in this case, it unbinds only that specific handler.
$(document).ready(function() {
var toggleStyleSwitcher = function(event) {
if (!$(event.target).is('.button')) {
$('#switcher .button').toggleClass('hidden');
}
};
$('#switcher').click(toggleStyleSwitcher);
$('#switcher-narrow, #switcher-large').click(function() {
$('#switcher').unbind('click', toggleStyleSwitcher);
});
$('#switcher-default').click(function() {
$('#switcher').click(toggleStyleSwitcher);
});
});
A shortcut is also
available for the situation in which we want to unbind an event handler
immediately after the first time it is triggered. This shortcut, called .one(), is used like this:
$(document).ready(function() {
$('#switcher').one('click', toggleStyleSwitcher);
});
This would cause the toggle action to occur only once.