jQuery Events: It’s .on()

Event management is one of jQuery’s strengths, but there’s a surprising amount of misunderstanding (see Click, Live, or Delegate) surrounding the lifecycle of events. In this article, we will talk about using .on() for its most basic purposes and for ‘translating’ from previous versions of jQuery to 1.7+. I do not discuss the optional data parameter or the .off() function. I would say this is a beginner-level article, for people looking for a re-phrasing of what’s already in the jQuery API docs; if that’s not you, you can probably get more from the docs themselves. Onward!

As of jQuery 1.7, it’s a lot easier to remember which binding function you need:

1.4.x+ 1.7
.bind() .on()
.live() .on()
.delegate() .on()

It’s not quite that simple (some function names are still around as aliases, for example) but you get the idea. .on() is meant to be your one-stop shop for event binding.

.on() Is all About the Listener

Grokking .on() is a simple matter of understanding that it’s the listener that’s important:

// 1. This replaces .bind(). .clickMe are self-listeners
$('.clickMe').on('click', function() { … }); 

// 2. This replaces .live(). The document is the listener
$(document).on('click', '.clickMe', function() { … }); 

// 3. This replaces .delegate(). Some ancestor element (here, #wrapper) is the listener
$('#wrapper').on('click', '.clickMe', function() { … }); 

The great thing is that the syntax is consistent (even dropped parameters have a logic):

  1. The first (.bind() analogy) says, “.clickMe nodes should listen for clicks on… hmmm… I don’t see anything specified… so… OURSELVES!”
  2. The second (.live() analogy) says, “The whole document should listen for clicks on .clickMe” elements.
  3. The third (.delegate() analogy) says, “We only need listen inside of #wrapper for those clicks (since as the author, I know they’ll never be elsewhere)!” By refining the listener like this, you get a more efficient event handler. Bonus: same syntax as #2 above, a unified syntax that makes so much more sense than the old way.

When to Use .on() In Different “Modes”

$(listener).on(event, function)

In my opinion, just like .bind() and .click(), this should only be used for the occasional one-off, and only when that node isn’t expected to be destroyed and replaced. Too often, people have used .click() (an alias for .bind(‘click’…) and now presumably for .on(‘click’…)) on a dynamic page, replaced old content with new, and wondered why their tabs, buttons, or other UI elements are suddenly not working. Worse, they try to fix it by running function to create all-new bindings. Stop the madness!

Another note along those lines: if you find yourself using an iterator to bind events, you should probably use an event listener other than self.

$(document).on(event, selector, function)

Yuk. Don’t do this. Ever. Unless document is literally the ONLY ancestor that will not be destroyed on your page, which I find extremely unlikely.

$(listener).on(event, selector, function)

I prefer this mode almost always. Even if a node with an event is a “one-off” and not likely to be destroyed, I will often choose this mode. If the listener is a close ancestor of the selector, the performance hit is negligible and I’m even more likely to choose this mode. If careful scrutiny reveals that it is extremely unlikely that destroying the element will negatively impact my application, I will use the first pattern (generally with the alias version such as .click() or .hover()).

Conclusion

Syntax changes always meet with mixed reception. How long are functions going to be grandfathered (aliased) for until they are truly deprecated? How are users to be notified and educated about the change? There are a lot of things to consider, and I’m sure there were (and are) arguments about the new functions in the API.

But I really think that .on() is justified. Not only does it unify syntax, but it provides other benefits not discussed in this article, making it far more flexible. I for one will be using it (and jQuery 1.7) effective next week, and will backport the functionality to a select handful of sites. I strongly recommend that you do the same!

(But really… for the love of all things good and holy… start binding sane listeners!)

Post Script: Why “.on()”?

My best guess is that they’re tapping into the familiar. An API should be intuitive, and people adopting the jQuery library may already be familiar with inlining events like “onclick” or “onmouseover” (a bad practice, but we’re all familiar with these events!). Personally I don’t think .on() is expressive enough (it is more accurately “listenFor”, but it does the trick.

6 Responses to “jQuery Events: It’s .on()”

  1. Jason

    Hi Greg!
    Great stuff! Can the .on be used similarly on a plugin type function like jRating? Or would the .on be included within the .js file?

            $(document).ready(function () {
                $(".RatingThing").jRating({
                    step: true, 
                    length: 11, 
                    decimalLength: 0, 
                });
            });
    
    Reply
    • Greg

      Hmm… I don’t know that plug-in, so it’s hard to say. They may have originally coded it to use .live(). It couldn’t be using .on() internally until they update it to use the new API.

      A quick look shows that they handle things with an initializer; all options and callbacks are set that way. So there are no plugin methods that you would call from within .on() anyhow.

      I guess the short answer is: the ideal place for .on() in jRating is inside the plugin itself, which won’t happen until the authors update it to use it.

      Reply
      • Jason

        Many thanks for the quick reply! Maybe I will consider rewriting it if time permits. At least this saves me the trouble of trying to get it to work when it won’t:)

        Reply
        • Greg

          Oh I don’t know about THAT. jRating will probably continue to work just fine even if it’s not using .on(). ;-)

          Reply
  2. Sam

    This is a great introduction article. One error, however: $(‘document’) won’t work (it will look for an element called “document”. You need $(document) (no quotes).

    Reply

Leave a Reply