Stop using $.bind, $.delegate and $.live, start using $.on!

Posted on: November 10, 2011Author: Rob

Last week the jQuery team released version 1.7 of their awesome library. They’ve made a lot of improvements and minor fixes. However, the most important change (in my eyes) are the new $.on() and $.off() Event APIs. By introducing these two new calls, they’ve unified Event handling no matter what you want to do with them. Pretty sassy.

You can read all about jQuery 1.7 in the release notes. This tutorial, however, will only focus on $.on() and $.off(). I will show you how you can use the new APIs instead of the already existing $.bind(), $.delegate() and $.live() APIs.

This tutorial is structured as follows:

Getting started

First we need to create a project directory where we can start playing around with jQuery. Create a new project directory (e.g jquery-project-tutorial). Because separation of content and presentation is important, I tend to create a subdirectories for my javascript and css files. For this tutorial, we’ll only need a javascript directory though, so let’s add it now. Your directory structure should look like this:

Download jQuery 1.7 and place it in the javascript directory. Once you’ve successfully downloaded jQuery, create a file called tutorial.js and place it in the javascript directory as well. Open the file in your favourite editor and add the following code:

$(document).ready(function(){
    alert('two legs!');
});

Create a file called index.html in your project directory. Open the file in your editor and create the following structure:

<!doctype html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>jQuery Tutorial</title>
    <script type="text/javascript" src="javascript/jquery-1.7.min.js"></script>
    <script type="text/javascript" src="javascript/tutorial.js"></script>
</head>
<body>
    <div id="main">
        <ul id="menu">
            <li><a href="#home">home</a></li>
            <li><a href="#link-1">link 1</a></li>
            <li><a href="#link-2">link 2</a></li>
            <li><a href="#link-3">link 3</a></li>
            <li><a href="#link-4">link 4</a></li>
            <li><a href="#link-5">link 5</a></li>
        </ul>
        <button>click button</button>
        <h1>Title</h1>
        <p>some content</p>
    </div>
</body>
</html>

Your directory structure should now resemble this:

Open the index.html file and our page should open and immediately display an alert dialog. Confirm the dialog and the page should now look something like this.

Congratulations! You now have a working project setup, time to get cracking on the new Event APIs.

Replacing $.bind() and $.unbind()

The $.bind() and $.unbind() APIs have been around for ages in jQuery. They can be used to start and stop listening to various Events. For example, if you want to listen for a click Event, you can simply do so by calling $.(element).bind(…). Let’s try the “oldschool” way in our project. Open tutorial.js and adapt the code as follows:

$(document).ready(function(){
    $('button').bind('click', function(){
        alert('you clicked me!');
    });
});

Refresh your browser and click the button to see your code in action. You can use $.unbind() to remove the handler. We’ll remove our handler after we’ve clicked the button once to prevent duplicate dialogs:

$(document).ready(function(){
    $('button').bind('click', function(){
        alert('you clicked me!');
        $(this).unbind('click');
    });
});

The $.bind() and $.unbind APIs are still supported, because the jQuery team wouldn’t want to break all existing code out there of course. However, from now on, we should be using $.on() and $.off(). Fortunately, we can use them exactly the same as the previous APIs:

$(document).ready(function(){
    $('button').on('click', function(){
        alert('you clicked me!');
        $(this).off('click');
    });
});

Sweet, we’ve successfully replaced $.bind() with $.on(). That was easy huh? Time to move on to our next Event APIs, $.delegate() and $.undelegate().

Replacing $.delegate() and $.undelegate()

With $.bind() and it’s corresponding $.on() version we have a great way to attach Event listeners to elements. However, what if we are creating a heavily “ajaxified” application where we perform lots of document manipulations. In such a situation, keeping track of all elements you want to listen to can become a hassle. Furthermore, if we have a list with, say, a hundred items, attaching Event listeners to all of them causes performance issues as well.

Fortunately we can delegate Events to parent elements. When something happens to an element, this element becomes very excited about it. It announces the Event to all its listeners, but it doesn’t stop there. It also informs its parent element about the Event, the parent element in turn informs its parent element and so on, until we’ve reached the final parent: document. We can use this behaviour to our advantage by attaching an Event listener to one of the parents in the chain and filtering for the desired element.

Let’s try this for our link elements. We can use $.delegate() to attach a handler to the main element instead of the elements and filter our Events to ensure we only react to the Events we want. Add the following lines of code to application.js.

$('#main').delegate('a', 'click', function(){
    alert($(this).attr('href'));
    $('#main').undelegate('a', 'click');
});

Now when you refresh your browser and click any of the links, its href attribute value will be shown in a dialog box. If we wanted to do the same with $.bind(), we would have added handlers to all link elements, now only one handler is required. Notice how we use undelegate to remove our listener, so we only show the alert once.

Now let’s adjust our code so we’ll use $.on() instead:

$('#main').on('click', 'a', function(){
    alert($(this).attr('href'));
    $('#main').off('click', 'a');
});

That’s it! The signature has only slightly changed compared to delegate, namely that the Event we are listing for (‘click’) and the elements we want to filter (‘a’) have swapped places. We also simply stop our listener by calling $.off(…).

If you want to read more information about Event delegation, you can take a look at this article.

Now that we’ve figured out how to use the new APIs for Event delegation, it’s time to look at our final Event APIs $.live() and $.die(). Keep on reading!

Replacing $.live() and $.die()

The $.live() and $.die() handlers haven’t been around for a very long time yet, but they’ve already been deprecated and will probably be removed entirely in jQuery 1.8.

So what does $.live do? It allows you to define elements you want to listen to in advance, even before they have been attached to the DOM. For example, if you want to perform an action for every input element, no matter which element, $.live would be a pretty decent candidate. Let’s add a $.live() handler and add an element we want to listen to dynamically. Add the following code to application.js:

$('input[type="checkbox"]').live('change', function(){
    alert($(this).attr('name'));
    $('input[type="checkbox"]').die('change');
});
$('#main').append('<input type="checkbox" name="testbox">');

Now you should see a new checkbox at the bottom of the page. Click it, and its name should be alerted to you in a dialog (‘testbox’). Notice how we added the Event listener before the element had been created.

But hold on, this looks familiar, remember how we can define delegated Events by attaching listeners to parent elements? So what happens if we add a delegated Event listener to the document root node? You guessed it, this does exactly what we need. Let’s adjust our code once more:

$(document).on('change', 'input[type="checkbox"]', function(){
    alert($(this).attr('name'));
    $(document).off('change', 'input[type="checkbox"]');
});
$('#main').append('<input type="checkbox" name="testbox">');

And there we are, all of our Event handling neatly performed in a unified way: $.on() and $.off(). Start using them now in all your new projects and your code will be that much better. And remember, for your existing projects $.bind() and $.delegate will be around for quite some time. However, $.live() and $.die() have been deprecated and will probably be removed in jQuery 1.8, so it would be wise to remove them were possible.

Code Summary

I always like it when the code in an article is summarized when all steps have been shown, so here we go:

index.html:

<!doctype html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>jQuery Tutorial</title>
    <script type="text/javascript" src="javascript/jquery-1.7.min.js"></script>
    <script type="text/javascript" src="javascript/tutorial.js"></script>
</head>
<body>
    <div id="main">
        <ul id="menu">
            <li><a href="#home">home</a></li>
            <li><a href="#link-1">link 1</a></li>
            <li><a href="#link-2">link 2</a></li>
            <li><a href="#link-3">link 3</a></li>
            <li><a href="#link-4">link 4</a></li>
            <li><a href="#link-5">link 5</a></li>
        </ul>
        <button>click button</button>
        <h1>Title</h1>
        <p>some content</p>
    </div>
</body>
</html>

javascript/application.js:

$(document).ready(function(){
    $('button').on('click', function(){
        alert('you clicked me!');
        $(this).off('click');
    });
    
    $('#main').on('click', 'a', function(){
        alert($(this).attr('href'));
        $('#main').off('click', 'a');
    });
    
    $(document).on('change', 'input[type="checkbox"]', function(){
        alert($(this).attr('name'));
        $(document).off('change', 'input[type="checkbox"]');
    });
    $('#main').append('<input type="checkbox" name="testbox">');
});

Happy Event listening!

  • Ben Frain

    I don’t use an awful lot of jQuery but this was a good explanation of how I had to change .bind() and .live() etc. Thanks a lot. Good post, clearly explained and well written.

    • http://www.two-legs.com/ Rob Been

      Thank you Ben, I appreciate the feedback :)