On - The New jQuery Event Handler

By Josh Wright on April 2013 in tips

 

jQuery has a new method of wiring up events. It's called 'on'.

It's actually been around since jQuery 1.7 which was released November 3, 2011. Here's my take on how it works and why I like it.

An Example

Say you have a page that lists Users. You can edit or delete each one:

Edit Kevin Durant Delete
Edit James Harden Delete
<table class='user-list'>
  <tr>
    <td><a class='btn-edit' href='#'>Edit</a></td>
    <td>Kevin Durant</td>
    <td><a class='btn-delete' href='#'>Delete</a></td>
  </tr>
  <tr>
    <td><a class='btn-edit' href='#'>Edit</a></td>
    <td>James Harden</td>
    <td><a class='btn-delete' href='#'>Delete</a></td>
  </tr>
</table>

Clicking 'Edit' pops up a modal where you can change the name. Saving a change causes that user's row to be re-rendered without having to re-render the whole page.

Clicking 'Delete' removes the user from the database and that row shrinks out of existence. Let's use that on James Harden.

What Is The Old Way?

Delete is a pretty strong action. You should add a confirmation popup that says, 'Are You Sure?'

Are You Sure?

So at the bottom of your page, you add this:

<script>
    $(function(){
        $('.btn-delete').click(function(){
            return confirm('Are You Sure?');
        });
    });
</script>

Why Is The Old Way BAD?

Looks right, but you've got some edge cases to deal with.

What happens when your edit modal re-renders the user's row? Your click event was bound on delete buttons that existed when the page loaded. But you just rendered a brand-new delete button that doesn't have a confirmation.

For a while, jQuery (& other libs) offered a .live() method that detected when new elements were loaded and automatically attached handlers. This was unreliable and required unwanted overhead. So you were stuck attaching handlers every time you change the content.

And there's another, more subtle problem. Say this page has endless scroll - you are binding a separate click event to every single delete button on the page. If you have lots of rows or lots of events per row, then you're murdering the browsers memory & processor.

What Is The New Way?

jQuery 1.7 introduced the .on() event system. The signature looks like this:

.on( events [, selector ] [, data ], handler(eventObject) )

First off - don't worry. You can still use it the old way. In fact, you can still use the old syntax.

//register click event
$('.btn-delete').click(function(){
       return confirm('Are You Sure?');
});

//same thing
$('.btn-delete').on('click', function(){
       return confirm('Are You Sure?');
});

But the power comes in when you use an extra selector.

//the new, awesome way
$('.user-list').on('click', '.btn-delete', function(){
    return confirm('Are You Sure?');
});

Here's how it works. This new syntax is built on the concept of event bubbling. So if you click a link, it will report the click to that link. If the link doesn't handle it, it 'bubbles' to it's parent. This continues until the event is handled or it runs out of parents (ie. the window).

So in our example, the click handler is actually attached to .user-list. Anytime a click event bubbles up to .user-list, it checks to see if the original element matches the selector .btn-delete. If it does match, then it fires our handler.

What Makes It Great?

It doesn't matter if the delete buttons exist or not when you set this handler. So if you add, edit, or remove delete buttons, then it doesn't matter. Once you click one, it just bubbles the event up until it hits .user-list and the handler fires.

It's also far more efficient because it only attaches one handler. Even if you are showing hundreds of users, you still only have one event registered. Beautiful.

Anything Else?

It's worth mentioning that the root element DOES have to exist when you set the handler, but not the selector. In our example above, .user-list has to exist but .btn-delete does not.

Also, my example used .user-list as the root element, but you could dig deeper. Using body as the root element will catch any un-handled event on the entire page. For example, this handler would fire when any a tag is clicked on the entire page.

$("body").on("click", "a", function(){
  alert($(this).text());
});

For more information, check out the jQuery docs