Using jQuery .on() and .off()

By Andi Smith - Thursday, November 10 2011

With the release of jQuery 1.7 on November 3rd came two new ways to attach event handlers - .on() and .off().

Whilst possibly not the most exciting new additions to jQuery’s large utility belt, these two additions do unify all types of (good) event handling in jQuery and will help you write tidier and more efficient code in the future.

In this article, I’ll give you a short introduction to both these handlers, and how you can move from your existing code of .bind(), .live() or .delegate() to .on() and .off(). Like everything in jQuery, the official documentation for .on() and .off() is pretty awesome and your best starting point for further questions, so I’ve included links throughout. We’ll also take a look at using .one() for single use event handling..

on()

Official documentation: http://api.jquery.com/on/

The on() event handler is designed to replace both the .bind() and .delegate() event handlers. It looks like this:

$(elements).on(events [, selector] [, data], handler);

Where:

  • events is the event type(s). It can be one or more space-separated event types, e.g. ‘click’ or ‘click keydown’. It can include an optional namespaces, e.g. “click.myPlugin”.
  • selector specifies the decendants of the elements that trigger the event. It is optional, and if it is not included the event will always trigger when the element event occurs.
  • data is any data you want to pass to the handler when the event is triggered. This is also optional, and if used is normally an object.
  • handler is the function to execute when the event is triggered.

You can also use an eventsMap to specify multiple events attached to one element.

$(elements).on(eventsMap [, selector] [, data]);

For example:

$('#container a').on({
  click: function(e) {
    e.preventDefault();
    console.log('item anchor clicked');
  },
  mouseenter: function(e) {
    console.log('enter!');
  }
});

Changing from .bind() to .on()

If you are currently using .bind() in your code, you have the easiest transition to .on(). Simply replacing .bind() with .on() will - in most cases - update your code.

// old way - .bind(events, handler);
$('#container a').bind('click', function(e) {
  e.preventDefault();
  console.log('item anchor clicked');
});

// new way (jQuery 1.7+) - on(events, handler);
$('#container a').on('click', function(e) {
  e.preventDefault();
  console.log('item anchor clicked');
});

Don’t feel you have to create the handler function within .on() - a tider way of coding the handler would be to call a separate function:

// handler function
function handleClick(e) {
  e.preventDefault();
  console.log('item anchor clicked');
}

$('#container a').on('click', handleClick);

This has the added benefit of allowing you to remove individual handlers later on using .off().

Changing from .live() to .on()

jQuery’s .live() has been deprecated as of version 1.7. .live() was originally used for attaching event handlers to elements that did not currently exist in the DOM. This was useful functionality for pages that dynamically generate or load content, but this method has been considered bad practice for some time now.

There are a number of problems with .live(), most are to do with poor performance on large pages, a slower response or unexpected behaviour due to .live() events being attached to the document element. Unfortunately there are still a large number of developers using .live() in their code. If you’re one of them, please stop. See “Why .live() is a Bad Option” for more information.

So what should you use instead for dynamically generated content? You should delegate your handlers using the new .on() handler, of course! Assuming you are adding dynamic content to a DIV element with ID “container”, instead of using:

// do not use! - .live(events, handler)

$('#container a').live('click', function(event) {

event.preventDefault();

console.log('item anchor clicked');

});

You should use the following:

// new way (jQuery 1.7+) - .on(events, selector, handler)

$('#container').on('click', 'a', function(event) {

event.preventDefault();

console.log('item anchor clicked');

});

This will attach your event to any anchors within the div#container element, reducing the scope of having to check the whole document element tree and increasing efficiency.

Please note - the narrower your selector matches are, the more efficient your code will be. Although this example uses an anchor, try to avoid attaching a handler to every anchor. It is better to give a class or id where possible.

Changing from .delegate() to .on()

If you were using .delegate(), your code will need minor re-organisation to work as .on(). With .on(), the parameters selector and eventType have switched positions.

// old way - .delegate(selector, events, handler);
$('#container').delegate('a', 'click', function(event) {
  event.preventDefault();
  console.log('item anchor clicked');
});

// new way (jQuery 1.7+) - on(events, selector, handler);
$('#container').on('click', 'a', function(event) {
  event.preventDefault();
  console.log('item anchor clicked');
});

This is the only change for converting from .delegate().

off()

Official documentation: http://api.jquery.com/off/

.off() is .on()’s evil twin undoing some (or all) of the good deeds .on() has done. It’s syntax looks like this:

$(elements).off( [ events ] [, selector] [, handler] );

With .off() all parameters are optional - using the parameters will allow you to be more specific in what event handling you wish to turn off. Specifying $(elements).off() will remove all event handlers from the element.

Moving from .unbind() to .off()

Simply replacing .unbind() with .off() will - in most cases - update your code.

// old way - .bind(events);
$('#container a').unbind('click');

// new way (jQuery 1.7+) - off(events);
$('#container a').off('click');

If you wanted to remove a particular handler and you have created a separate handler function you can use:

$('#container a').off('click', handleClick);

Using this method, it’s possible to remove some of the event handler’s functionality without removing all of it.

Changing from .die() to .off()

As you are no longer using .live() anymore, you should also no longer use .die().

// do not use! - .die(events)
$('#container a').die('click');

// new way (jQuery 1.7+) - .on(events, selector, handler)
$('#container').off('click', 'a');

As with live(), die() has been deprecated. That’s right, it’s time for .die() to die!!!

Changing from .undelegate() to .off()

As with .on(), the order of these parameters has changed for consistency.

// old way - .undelegate(selector, events);
$('#container a').undelegate('a', 'click');

// new way (jQuery 1.7+) - off(events, selector);
$('#container a').off('click', 'a');

one()

Official documentation: http://api.jquery.com/one/

It’s entirely possible that you may want your event to fire only once. jQuery’s one() event handler attachment will handle attach the event for the first click and unattach it for subsequent clicks without you needing to write any additional code. Although one() has been included in jQuery for some time, I’ve rarely seen it used. In version 1.7 it has been updated to allow for delegation.

$(elements).one(events [, selector] [, data], handler);

The parameters are the same as for .on().

Now you’ve read an introduction to .on() and .off(), it’s time to start using them in the real world. I hope this article has provided a good starter and encouraged you to investigate these new additions. If you need further information I recommend reading the official documentation, but I’m happy to answer any questions below.

Good luck!

Andi Smith

Andi Smith is a web developer from London, United Kingdom. He likes to build highly performant websites which innovate with genuine value.