Using jQuery .on() and .off()
Posted on
JavaScript.
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, and there is a live jsFiddle demo for you to take a look at too.
What is event handling? The majority of JavaScript code is triggered from an event. The page loads, an event fires. You press a key or click a button, an event fires. You move your mouse, an event fires. Event handling is telling the JavaScript what to do in these situations (how to handle the event that has been fired). Visit Quirksmode to read more about 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:
|
1 |
$(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.
|
1 |
$(elements).on(eventsMap [, selector] [, data]); |
For example:
|
1 2 3 4 5 6 7 8 9 |
$('#container a').on({
click: function() {
event.preventDefault();
console.log('item anchor clicked');
},
mouseenter: function() {
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.
|
1 2 3 4 5 6 7 8 9 10 11 |
// old way - .bind(events, handler);
$('#container a').bind('click', function(event) {
event.preventDefault();
console.log('item anchor clicked');
});
// new way (jQuery 1.7+) - on(events, handler);
$('#container a').on('click', function(event) {
event.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:
|
1 2 3 4 5 6 7 |
// handler function
function handleClick(event) {
event.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:
|
1 2 3 4 5 |
// do not use! - .live(events, handler)
$('#container a').live('click', function(event) {
event.preventDefault();
console.log('item anchor clicked');
}); |
You should use the following:
|
1 2 3 4 5 |
// 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.
|
1 2 3 4 5 6 7 8 9 10 11 |
// 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:
|
1 |
$(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.
|
1 2 3 4 5 |
// 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:
|
1 |
$('#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().
|
1 2 3 4 5 |
// 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.
|
1 2 3 4 5 |
// 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.
|
1 |
$(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.
Update: I’ve created a demo of all these new event handlers on jsFiddle.
Good luck!
Enjoyed this post? Follow me on Twitter!
38 Comments
-
Mateus Souza
Posted on
$(‘#container a’).on(‘click’, ‘a’, …)?
So… the HTML is #container > a > a
I guess this should be:
$(‘#container’).on(‘click’, ‘a’, …)
-
Kevin
Posted on
Thanks for the article. This helped a LOT.
I think your delegate example code is not correct, but I’m no expert.
-
Cezar Luiz
Posted on
Cool article Andi! Where’s the demo? Haha, thanks!
-
Cezar Luiz
Posted on
Andi, what about performance, which is better, or delegate on? But from what I saw, the event on() is used only for dynamic content? Replaced in a more easy to delegate() and live()? Or I can use in other situations?
Thanks Andi!
-
Srinivasan
Posted on
Thanks for this article,it’s very useful to start code using jquery 1.7
-
Jordan Lev
Posted on
Do you know if it’s recommended to use .on() in place of the specific event shortcuts such as .click(), .hover(), etc.?
Or perhaps jquery 1.7 has changed the underlying code of .click() etc. to use .on() now, so it doesn’t matter?Thanks for the great blog post — it makes a lot of sense!
-
Pawel
Posted on
@Jordan Lev
if you’re using jquery 1.7 on your site, just type in the console:
console.debug($.fn.click.toString());you’ll see:
function (a, c) { c == null && (c = a, a = null); return arguments.length > 0 ? this.on(b, null, a, c) : this.trigger(b); }
so you do not have to change them if you do not want
-
Casper
Posted on
Great rundown, thanks!
-
Alex M
Posted on
Brilliant. Great reference for us all. You’re a legend!
-
Imran
Posted on
Gr8 article i will apply this for my blog
Your blog design is very nice
-
Evan
Posted on
Thanks for the tips, I’d been using ‘live’ and have now been reprimanded =), I like ‘on’ better anyways.
-
Shripad K
Posted on
Another awesome thing is that you can remove all listeners from all elements on your page by doing $(‘*’).off()
This is especially useful if you are dealing with html5history, where you need to replace the page and rebind all elements.
-
digitalpbk
Posted on
Thank you for the introduction to the new jquery api ,.. time for some search and replace..
-
daGrevis
Posted on
Very useful. Thanks for writing it.
-
Andy B
Posted on
I am trying to use $(“#content”).on(“submit”, “form”, function(e) { var formData = $(this).serializeArray(); and make a $.ajax call posting formData and reload #content with results }. It works expect that it does not reattached the event handler (submit) after the ajax reload. If I use $(“#content”).on(“submit”, function(e) { … } it reattaches but I cannot get formData to work in that case. Any suggestions? Thanks!
-
Elad Katz
Posted on
Great explanations. thanks!
-
Ville V. Vanninen
Posted on
Thanks for the clear explanations, very helpful.
-
Richard Sweeney
Posted on
Great article, really clearly explained. The jQuery site is so horrible, it’s can be really off-putting, whereas your nice, clean site made it all the more easy to digest the info! Many thanks.
-
Anjieya
Posted on
Many article talk about .click, I didn’t find about .change?
How about .change what new in 1.7?
-
Anjieya
Posted on
@andy thank you… will try it, I used .change for chained combo box, it was worked in 1.4 but still didn’t success for in 1.7, maybe need upgrade and fix it.
-
AJ Engwall
Posted on
I’m having trouble using .on() with toggle(). Is this not supported?
ex.
$(“table”).on(“toggle”,”tr”,function(){
$(this).css(‘background-color’,'yellow’);
},function(){
$(this).css(‘background-color’,'white’);
}
);
-
Paul B
Posted on
Hi Andi,
I seem to have the same problem as Andy B has, maybe you could help us both out:
I am dynamically adding text input fields and this works fine but when I’m trying to read out the inserted’s field value using i.e. $(‘#text_input_id’).val() i get nothing. By cycling through all elements of the page I found that the id doesn’t even get parsed, I suppose because the field was not existing at $(document).ready time.
Do you know a workaround for this?Thanks,
Paul
-
Paul B
Posted on
After 2 days of searching I finally made it. For all you guys out there having same the trouble: you need to read the dynamic generated input’s value from outside of your $(document).ready{} method, because as I said before at the time when $(document).ready is executed, the input does not exist.
-
Alex
Posted on
I would like to share an issue I had and get some feedback on it from “the master”
Since I am a bit of a n00b here. (if you could email me that would be awesome)So, I have a container DIV that I am dynamically loading content into with the jQuery .load() command. Part of that content that gets loaded is a button that runs a function.
Typically, when using the live() command, it would handle the dynamically added content since live() interacts with the document.
When I tried to replace my .live() commands with on(), it broke my button. The function never fired. As if I wasn’t registering the dynamic content.
It wasn’t until I changed the scope of my selector to point to the “document” that I was able to get the button to work. For example:
//First load my content and button with load(); then,
$(document).on(‘click’, ‘myDynamicButton’, function() {})What are your thoughts on this??
-
Alex
Posted on
Also interesting to note, is that since this was on an iPad, the button didn’t work until I added the touchstart command.
$(document).on(‘click touchstart’, ‘#myDiv’, function() {})
Weird, huh?
-
Petr
Posted on
Alex is right, it really seems you have to use a selector for the added elements to work. So $(“.item”).on(“click”,function) would work for original elements with class “item” but not for newly added ones. If you instead use $(“#items”).on(“click”,”.item”,function) it suddenly works for dynamically added elements too (presuming “#items” is a parent element to “.items” elements.
It’s a bit strange, not sure if it is a bug or there is a deeper meaning for it.
-
Travis
Posted on
Hi, thank you for your great article!!
I have a question about one(), pleas help.
Would it be possible to add one() to dynamically rendered elements just like on()?
Thank you in advance.
-
Fintel
Posted on
This is brilliant stuff. I have been looking for simple examples. The jQuery website is good but this is better. I could never figure out how to replace live with on but now it seems so easy. Thanks again
-
John
Posted on
Thanks for clearing things up so nicely !
-
kid
Posted on
But why? Why would they add on and off this late in the game? You say it creates ‘tidy, more efficient’ code. How so? You explained it well when it comes to Live and I agree, Live is a poor construct. But what was so wrong with delegate and bind?
-
Edouard
Posted on
Hi,
I am using the following code to highlight table rows :$(document).ready(function(){ $('#content-manager-table tr').hover( function() { $(this).addClass('highlight'); }, function() { $(this).removeClass('highlight'); } ) });It works well, but when the table is updated via ajax, the highlight is not done.
I guess the ‘problem’ comes from the way i call the function :
$(document).ready(function(){…
and i wonder if .on could help me in any way.If someone could help me updating my code ?
Thanks,
Edouard