Chapter 8. Events

Ariel Flesler

Introduction

Events are the main method of communication between a user and a website or web application. Most of our JavaScript/jQuery coding will be run in response to a variety of user and browser events.

By user events, I mean basically keyboard and mouse interaction like click, mousedown, keypress, etc. Browser events are mainly DOM events like document.ready, window.onload, and many other events related to DOM elements.

When coding Ajax applications, we also have custom jQuery Ajax events that are dispatched during the process of an Ajax request, that is, ajaxSend, ajaxComplete, ajaxError, and some more.

jQuery’s API is very consistent, especially when it comes to events. Attaching a handler to any kind of event is done using the same code structure:

jQuery( listener).bind( 'eventName', handlerFunction);

This syntax also applies to a fourth category that I haven’t mentioned yet. jQuery’s event system can be used for event-driven programming[1] in which you can create your own custom events that can be bound and triggered as regular ones.

jQuery also provides a shortcut method for most common browser and Ajax events. A model call using a shortcut would look like this:

jQuery( listener).eventName( handlerFunction);

When using bind(), eventName will be a string wrapped in either single or double quotes. When using the shortcut, you simply put the event’s name as the jQuery method’s name.

Here’s an example of binding a click handler, with and without the shortcut:

// Using bind()
 jQuery('div').bind('click',function(e){...});
 // Using the shortcut
 jQuery('div').click(function(e){...});

During this chapter, I’ll use the shortcuts when available, just because they’re shorter and easier to read, in my opinion. Both work equally, and there’s no advantage to using the shortcut other than clarity and brevity; it’s simply a matter of taste.

I’ll assume that you already read Chapter 1, where the document.ready event is explained in detail (Recipe 1.2). If you have any doubt about its use, do consult that recipe.

I also want to clarify that when I use the term plugin, for most cases I mean “plugins, widgets, or simply blocks of code.” Most jQuery users tend to organize their code into plugin-like structures, usually adding names to jQuery’s namespace.

Finally, jQuery’s event module was highly modified in 1.3. I will always mention when something needs to be done differently, according to what jQuery version would be used.

8.1. Attaching a Handler to Many Events

Problem

In many common situations, one needs to bind the same handler function to more than one event (on the same element, that is). You could always do something like this:

jQuery('div').click(function(e){
            alert('event'),
         })
         .keydown(function(e){
            alert('event'),
         });

That is not such a problem if the function is short, but for longer blocks of code, repeating them over and over won’t be that trivial and is definitely not the best approach.

Solution

There’s more than a single solution to this simple but recurrent problem.

One way to solve it without repeating yourself too much would be as follows:

function handler(e){
    alert('event'),
 }

 jQuery('div').click(handler)
         .keydown(handler);

Defining a function once and then referring to it multiple times is not a bad approach, but there’s an even simpler one provided by jQuery.

bind() accepts a list of events separated by spaces. That means you can solve the previous problem like this:

jQuery('div').bind'click keydown', function(e){
    alert('event'),
 });

Discussion

You can also apply this behavior to unbind() and one().

To unbind a certain function, you need to have a reference to it, so even if you are using the multievent feature, you still need to keep a reference to the handler. If you don’t pass the function to unbind(), then any other event handler bound to that event will be removed as well:

function handler(e){
    alert('event'),
 }

 jQuery('div').bind('click keydown', handler);

 // ...

 jQuery('div').unbind('click keydown', handler);

8.2. Reusing a Handler Function with Different Data

Problem

You’ve come into a situation where you have many bindings, and the handler functions look pretty similar. It doesn’t matter whether these bindings are applied to different element/event combinations. The thing is, you don’t want to repeat yourself over and over (who does?).

Here’s an example:

jQuery('#button1').click(function(e){
     jQuery('div.panel').hide();
     jQuery('#panel1').show();
     jQuery('#desc').text('You clicked the red button'),
 });

 jQuery('#button2').click(function(e){
     jQuery('div.panel').hide();
     jQuery('#panel2').show();
     jQuery('#desc').text('You clicked the blue button'),
 });

 jQuery('#button3').click(function(e){
     jQuery('div.panel').hide();
     jQuery('#panel3').show();
     jQuery('#desc').text('You clicked the green button'),
 });

As you can see, the only differences noticed on each handler are the color and the panel to show. The amount of code would grow as you add more buttons or each time the handler functions get larger.

Solution

bind() accepts an optional data argument to be bound together with each specific handler function. The data values will be accessible from within this function by accessing event.data[2] where event is the event object argument provided by jQuery.

Note that this value can be anything...an array, a string, a number, or an object literal.

It’s a common approach to pass an object literal, even if you are just passing one value, to make the code more readable. This way, the name you give this single attribute within the object will make your code a little more self-explanatory.

Discussion

event.data is used to provide precomputed values to a function, which means the values you will be passing to bind() need to be already available at binding time. To handle more “dynamic” values, there’s another way that we’ll learn about in Recipe 8.5.

The solution to the previous problem could look something like this:

function buttonClicked(e){
     jQuery('div.panel').hide();
     jQuery('#panel'+e.data.panel).show();
     jQuery('#desc').text('You clicked the '+e.data.color+' button'),
 }

 jQuery('#button1').bind('click',{panel:1, color:'red'}, buttonClicked);
 jQuery('#button2').bind('click',{panel:2, color:'blue'}, buttonClicked);
 jQuery('#button3').bind('click',{panel:3, color:'green'}, buttonClicked);

Of course, you could make this even shorter by using a loop. This approach is called a macro by some coders, and it’s a very common approach for jQuery code.

These macros will surely reduce the code length and can sometimes improve code readability. Some other times, they’ll just make your code completely unreadable, so use them with caution.

Here’s how you could do it:

jQuery.each(['red','blue','green'], function(num, color){
     num++; // it's 0-index based
     jQuery('#button'+num).bind('click',function(e){
         jQuery('div.panel').hide();
         jQuery('#panel'+num).show();
         jQuery('#desc').text('You clicked the '+color+' button'),
     });
 })

As you can see, I haven’t used the data argument because we don’t really need it. The code is now somewhat shorter, but not that much, and it’s not more readable.

The conclusion is that both approaches can be used on this kind of situation. Depending on the problem, one could be better (shorter, more readable, easier to maintain) than the other.

8.3. Removing a Whole Set of Event Handlers

Problem

So, you’ve made a plugin-like block of code that binds many event handlers to certain DOM elements.

Later, you want to clean them all up in order to dispose the plugin completely.

This could get a little lengthy if you added many handlers. Maybe you don’t even have access to the bound handlers because they belong to another local scope.

You can’t unbind every handler for a certain event (or any existing event), because you could be deleting other handlers that you didn’t take into account.

Solution

Use a unique namespace for each plugin you make. Any handler bound within this plugin must be added with this namespace.

Later, when cleaning up, you just need to “unbind the whole namespace,” and all the related event handlers will go away with one single line of code.

Discussion

How to bind with a namespace?

To add a namespace to an event type, you simply add a . followed by the namespace name.

Since jQuery 1.3, you can add more than one (namespace) per event.

This is how you would bind the click and mousedown functions with a namespace:

jQuery.fn.myPlugin = function(){
    return this
        .bind('click.myPlugin', function(){
             // [code]
        })
        .bind('mousedown.myPlugin', function(){
             // [code]
        });
};

How to clean up my plugin?

To dispose the bindings above, you would do:

jQuery.fn.disposeMyPlugin = function(){
    return this.unbind('.myPlugin'),
};

8.4. Triggering Specific Event Handlers

Problem

You need to trigger an event on a certain element (or many). This element belongs to one or more plugins so it may have event handlers bound to this event.

The problem is that this event is a common one, like click or mousedown. Simply triggering the event could run other event handlers that you didn’t expect.

Solution

On the same principle as the previous recipe, namespaces can be used for triggering as well. When binding, you need to make sure you add a unique namespace to each set of handlers.

This can also be used for the opposite situation; if you need to trigger any event except those with a namespace, you can use the ! operator. An example of this will be shown in the discussion.

Discussion

How to trigger handlers with a certain namespace?

Now, say you want to programmatically trigger the click event bound by the plugin myPlugin. You could simply trigger the click event, but that would be a bad approach, because any other handler bound to the same event would get fired as well.

This is how to do this properly:

jQuery.fn.runMyPlugin = function(){
     return this.trigger('click.myPlugin'),
 };

How to trigger handlers that do not have a namespace?

On the contrary, maybe you need to trigger a click (or any other event), but the target element belongs to one or more plugins. Triggering an event could run undesired event handlers, and that would cause problems that will be pretty hard to debug.

So, assuming all the plugins did use a namespace, this is how to trigger a click safely:

jQuery('div.panels').trigger('click!'),

8.5. Passing Dynamic Data to Event Handlers

Problem

You want to pass certain values to an event handler, but they’re not known at “binding time,” and they would change with each call to the handler.

Solution

There are two ways of solving this problem:

  • Passing extra arguments to trigger()

  • Passing a custom event object to trigger()

Both approaches work, and neither is clearly better than the other. The second approach was a little awkward to use before jQuery 1.3. Since this version, it has become pretty straightforward and less problematic. I’ll explain each option in detail in the “Discussion” section.

Passing data to the handler, instead of making the function grab it from somewhere (global variables, jQuery namespace, etc.), makes the code easier to maintain because you keep handler functions simple and agnostic from the environment.

This also allows you to reuse the same handler for many situations.

Discussion

Passing extra arguments

trigger() can receive one or more values that will be passed on to the triggered handlers.

These values can be of any type and any amount. When you have more than one, you need to wrap them with an array:

jQuery('form').trigger('submit', ['John','Doe', 28, {gender:'M'}]);

The bound function for the preceding case would be something like this:

jQuery('form').bind('submit', function(e, name, surname, age, extra){
    // Do something with these arguments
});

This approach is simple and easy to read. The problem is, it looks pretty bad when you need to receive many arguments; I personally wouldn’t go beyond four to five.

It’s also kind of misleading if the reader is used to the common function(e){ } kind of function.

You start to wonder, where do these other arguments come from ?

Some more examples

Used within a programmatic event:

jQuery('#slideshow').bind('add-image', function(e, src){
    var $img = jQuery('<img />').attr('src', src);
    jQuery(this).append($img);
});
jQuery('#slideshow').trigger('add-image', 'img/dogs4.jpg');

Used within a real event:

jQuery('#button').bind('click', function(e, submit){
    if( submit )
        // Do something
    else
        // Do something else
});
jQuery('#button').trigger('click', true);

Passing a custom event object

If you choose to pass a custom event object instead, each value you pass has to be accessed as an attribute on the event object received by the handler.

This means that, no matter how many data you’re passing, the handler will always have only a single argument, the event object.

This is already an advantage over the first approach, because it makes the function declaration less verbose.

As mentioned, this approach is much nicer to use since jQuery 1.3. Here’s how you’d code the first example with a custom object:

jQuery('form').bind('submit', function(e){
    // Do something with e.name, e.surname, etc.
});
jQuery('form').trigger({
    type:'submit',
    name:'John',
    surname:'Doe',
    age: 28,
    gender:'M'
});

Passing an object literal is actually a shortcut to creating an instance of jQuery.Event.[3] This is the alternative way:

var e = jQuery.Event('submit'), // the new operator can be omitted
e.name = 'John';
e.surname = 'Doe';
e.age = 28;
e.gender = 'M';
jQuery('form').trigger(e);

You can, of course, use jQuery.extend instead of setting one attribute at a time.

You do need to create an event object yourself if you plan on retrieving data from this object after the call to trigger(). That’s, by the way, a cool technique to pass information from the handler to the caller (we’ll get into this in the next chapter).

What’s the difference with event.data?

Using event.data is useful for static values that are accessible at the time when the function was bound. When the data you need to pass must be evaluated later (or each time), event.data won’t do for you.

8.6. Accessing an Element ASAP (Before document.ready)

Problem

You need to gain access to a certain DOM element as soon as possible.

Using document.ready isn’t fast enough; you really want to control this element before the page finishes rendering.

Issues like this are especially noticeable on large pages, where the document.ready event takes longer to be reached.

Solution

This is a very common and generic problem that can be solved in many different ways.

There’s one approach that works for all of them, but it requires polling the DOM so it adds overhead to the page-rendering process (definitely undesirable!).

These are some of the usual problems where one could rely on polling:

  • Hide an element right away, before it is rendered (or another style operation)

  • Bind event handlers to an element ASAP so that it quickly becomes functional

  • Any other situation

We’ll discuss what’s the better approach for each situation in the “Discussion” section.

Discussion

Hide an element right away (or another style operation)

So, your problem is directly related to styling, you want to apply a conditional styling to an element, and this condition needs to be evaluated by JavaScript.

The right way to go about this is adding a specific CSS class to an element that is quickly accessible, like the <html> element, and then style the element accordingly.

Do something like this:

<!DOCTYPE html>
<html>
<head>
 <style type="text/css">
    html.no-message #message{ display:none; }
 </style>
 <script src="assets/jquery-latest.js"></script>
 <script type="text/javascript">
     // Bad
     jQuery(document).ready(function($){
        $('#message').hide();
     });
     // Correct
     jQuery('html').addClass('no-message'),
     // or...
     document.documentElement.className = 'no-message';
 </script>
</head>
<body>
    <p id="message">I should not be visible</p>
    <!--
      Many more html elements
    -->
</body>
</html>

Bind event handlers to an element ASAP

Very often we have this large page with interactive elements, like buttons and links.

You don’t want those elements to just hang in there, without any functionality attached while the page loads.

Luckily, there’s a great concept called event delegation that can save the day. Event delegation is easy to implement with one of several jQuery plugins, and since jQuery 1.3, a plugin is no longer needed, because it has been added to the core jQuery file.

You can now bind event handlers to elements that still don’t exist by using the method live().[4] That way, you don’t need to worry about waiting for the element to be ready in order to bind the events.

To read more about event delegation, check Recipe 8.10.

Any other situation

Your problem isn’t about styling or about events. Then you, my friend, fall into the worst group.

But don’t panic! There’s a better solution than polling if you’re concerned about performance. I’ll explain it at last.

Polling

Polling can be implemented with a simple interval (setInterval) that checks for an element, and once found, a certain function is run, and the interval needs to be cleared.

There are two plugins that can aid you with this. One is LiveQuery,[5] which has an option to register a function to be run for each newly found element that matches a selector. This approach is pretty slow but supports the whole set of selectors.

There’s another plugin called ElementReady[6] that will also handle this situation properly.

It lets you register pairs of id/function, and it will poll the DOM. Once an id is found, the function will be called, and the id is removed from the queue.

This plugin implements, probably, the fastest approach to detect elements, that is, using document.getElementById. This plugin is pretty fast but only supports ids.

Customly positioned scripts

The whole document-ready concept means “after the html is parsed.” This means the browser reached the body’s closing tag, </body>.

In other words, instead of using document.ready, you could simply put your scripts right before </body>.

You can apply the same principle to other parts of the DOM: you can add a <script> right after the element you want to access, and you can know, for certain, that it will be already accessible from it.

Here’s an example:

<!DOCTYPE html>
<html>
<head>
 <script src="assets/jquery-latest.js"></script>
</head>
<body>
    <p>The time is <span id="time">&nbsp;</span></p>
    <script type="text/javascript">
    jQuery('#time').text( new Date().toString() );
    </script>
    <!-- Many more html elements -->
</body>
</html>

As you can see, no polling was needed in this case. This is a feasible solution if you don’t need to use it a lot or you’ll be adding tons of scripts to the page.

8.7. Stopping the Handler Execution Loop

Problem

You have several handlers bound to the same element/event combination.

You want to, from within a handler, prevent the rest from being called, something like what event.stopPropagation()[7] does. The problem is that event.stopPropagation() only works for elements that are below the current element in the DOM hierarchy.

Solution

Since jQuery 1.3, event objects passed to handlers have a new method called stopImmediatePropagation().[8] This method will do just that, and no subsequent event handler will be notified of the current event. It will also stop the event’s propagation, just like stopPropagation() does.

This method has been taken from ECMAScript’s DOM level 3 events specification.[9]

If you want to consult the event object, to know whether this method has been called, you can do so by calling event.isImmediatePropagationStopped(),[10] which will return either true or false.

Discussion

Examples

Simple form validation

stopImmediatePropagation() can cancel the actual submit binding(s) if a certain situation is met:

jQuery('form')
    .submit(function(e){
       e.preventDefault(); // Don't submit for real
       if( jQuery('#field').val() == '' )
           e.stopImmediatePropagation();
    })
    .submit(function(e){
       // Only executed if the function above
       // didn't call e.stopImmediatePropagation
    });
Killing all events

It can also be useful for disabling elements or blocking containers temporarily:

(function($){

function checkEnabled(e){
    if( jQuery(this).is('.disabled') ){
        e.stopImmediatePropagation(); // Stop all handlers
        e.preventDefault();
    }
};

jQuery.fn.buttonize = function(){
    return this.css('cursor','pointer')
              .bind('click mousedown mouseup',checkEnabled};
};

})(jQuery);

Disadvantages of this approach

While this new feature could be a lifesaver in some situations, you must be aware that basing your logic on this behavior isn’t all that safe. When you rely on this feature, you assume that the handlers will be executed in the order you expect and that no other handlers will get in the way.

While events bound with jQuery are executed in the same order they’re added, it’s not something the API strongly supports, meaning it could fail in some browsers or some special situations. It’s also possible that bindings from different plugins could collide because one could call stopImmediatePropagation() and the other wouldn’t get executed. This could cause unexpected problems that could take a long time to debug.

The conclusion is, don’t be afraid to use stopImmediatePropagation() if it really suits your problem, but do use it with caution and double-check all the event handlers involved.

You should rather think twice before using it in these situations:

  • The listener is a “popular” DOM element that is also used by other plugins.

  • The event is a common one like click or ready. There’s a greater chance of collisions.

On the other hand, it should be pretty safe to use it in these situations:

  • The listener is a DOM element that is dynamically created and used merely by one plugin.

  • The event is a custom event like change-color or addUser.

  • You intentionally want to stop any bound handler (like in the second example).

8.8. Getting the Correct Element When Using event.target

Problem

Your code is relying on the event.target[11] property of an event object, most likely in combination with event delegation, where one single event handler is bound to a container and it manages a variable number of descendant elements.

In some cases, you don’t seem to be getting the expected behavior. event.target is sometimes pointing to an element that is inside the one you expected.

Solution

The event.target property refers to the element that got the event, that is, the specific element.

This means that if, for example, you have an image inside a link and you click the link, the event.target will be the image, not the link.

So, how should you work around this? If you’re not working with event delegation, then using this (scope of the function) or event.currentTarget (since jQuery 1.3) should do. It will always point to the element that has the event handler bound.

If you’re indeed using event delegation, then you’ll need to find the parent element you were expecting.

Since jQuery 1.3, you can use closest().[12] As specified in its documentation, it will return the closest element, beginning with the current element and going up through the parents, that matches a certain selector.

If you’re using an older version of jQuery, you can simulate closest() with something like this:

jQuery.fn.closest = function( selector ){
    return this.map(function(){
        var $parent = jQuery(this).parents();
        return jQuery(this).add($parents).filter( selector )[0];
    });
 }

This could be improved a little for performance, but this simple version should do for general purposes.

Here’s a small example of a very common situation using closest():

jQuery('table').click(function(e){
    var $tr = jQuery(e.target).closest('tr'),
    // Do something with the table row
 });

Discussion

event.target is one of the event object’s properties normalized by jQuery’s event system (event.srcElement on IE).

So, how come an event is triggered on this target element and your event handler is called even when bound to an ancestor ? The answer is Event bubbling.[13]

Most standard DOM events do bubble.[14] This means that, after the event was triggered on the target, it will go up to its parent node and trigger the same event (with all its handlers).

This process will continue until either it reaches the document or event.stopPropagation() is called within an event handler.

Thanks to event bubbling, you don’t need to always bind event handlers to specific elements; instead, you can bind to a common container once and handle them all from there. This is the principle of event delegation.

8.9. Avoid Multiple hover() Animations in Parallel

Problem

We all have fallen for this at least once. You set up something like this:

jQuery('#something').hover(
    function(){
        // add some cool animation for jQuery(this)
    },
    function(){
        // Revert the cool animation to its initial state
    }
);

For example, you could be enlarging an element each time the mouse rolls over it and then shrinking it to its initial size once the mouse rolls out.

All goes well until you quickly move the mouse over and out of the element and...what?!

The jQuery('#something') element suddenly gets resized back and forth many times until it finally stops.

Solution

The solution is indeed simple, too simple, but the problem is so recurrent that I really consider this solution a useful one.

What you need to do in order to avoid this nasty effect is simply kill all existing animations on the element before you create a new one.

To do so, you have to use jQuery’s stop() method. It will (as the name says) stop the current animation and, optionally, remove the following ones as well.

Discussion

Example

I’ll show you an example of animating the opacity CSS property, but it works the same for any other property:

jQuery('#something').hover(
    function(){
        jQuery(this).stop().animate({opacity:1}, 1000);
    },
    function(){
        jQuery(this).stop().animate({opacity:0.8}, 1000);
    }
 );

This also works for custom jQuery animations, like slideUp(), slideDown(), fadeIn(), etc.

This is the former example using the fade methods:

jQuery('#something').hover(
    function(){
        jQuery(this).stop().fadeTo( 1, 1000 );
    },
    function(){
        jQuery(this).stop().fadeTo( 0.8, 1000 );
    }
 );

Not there yet

There’s still another related problem that could arise in a situation like this:

jQuery('#something').hover(
    function(){
        jQuery(this).stop()
            .fadeTo( 1, 1000 )
            .animate( {height:500}, 1000 );
    },
    function(){
        jQuery(this).stop()
            .fadeTo( 0.8, 1000 )
            .animate( {height:200}, 1000 );
    }
 );

If you try this code and move the mouse quickly, the element will get animated crazily again, but only its height (not the opacity) will animate.

The reason is simple; jQuery animations get queued by default. This means that if you add several animations, they’ll get executed in sequence.

stop() by default only stops (and removes) the current animation. In our last example, only the opacity animation will be removed each time, leaving the height animation in the queue, ready to run.

To work around this, you have to either call stop() one more time or pass true as the first argument. This will make stop() clean all the queued animations as well. Our hover code should look like this:

jQuery('#something').hover(
    function(){
        jQuery(this).stop(true)
            .fadeTo( 1, 1000 )
            .animate( {height:500}, 1000 );
    },
    function(){
        jQuery(this).stop(true)
            .fadeTo( 0.8, 1000 )
            .animate( {height:200}, 1000 );
    }
 );

8.10. Making Event Handlers Work for Newly Added Elements

Problem

You’ve bound one or more event handlers, and they suddenly stop working.

It happens after new elements are added dynamically by an Ajax request or simple jQuery operations (append(), wrap(), etc.).

Note

This problem is incredibly common, and we’ve all fallen for this at least once.

I’ll explain the theory behind it in the “Discussion” section. If you feel you need to understand this well before heading to the solutions, check Why do event handlers get lost ? first.

Solution

There are two possible solutions for this recurring problem, each with its own pros and cons:

Rebinding

This approach requires you to call bind() again and again, every time new elements are added.

It’s pretty easy to implement and doesn’t require any plugin or new method.

You can simply have all the bindings in a function and call it again after each update.

Event delegation

It relies on event bubbling.[15] This is fast and light but requires a little understanding and can be (just) a little tricky at times.

Since jQuery 1.3, there’s built-in support for event delegation. Using it is as simple as using the new live() method instead of bind().

Discussion

Why do event handlers get lost ?

JavaScript, as opposed to CSS, isn’t a declarative language. You don’t describe behaviors, and they get “automagically” applied.

JavaScript, like most other programming languages, is imperative. The developer specifies a sequence of actions to perform, and they get applied as the line of code is reached.

When you add a piece of code like this:

function handler(){
    alert('got clicked'),
}
jQuery('.clickable').bind('click', handler);

this is what you’re “doing”:

  1. Look for all elements with a CSS class “clickable” and save it to the collection.

  2. Bind the “handler” function to the click event of each element in the collection.

If JavaScript/jQuery were interpreted declaratively, the previous code would mean the following:

  1. Each time an element with CSS class clickable is clicked, run the function handler.

However, because JavaScript/jQuery is interpreted imperatively, the only elements that will get bound are those that match the selector at the time it is run. If you add new elements with a clickable class or you remove the class from an element, the behaviors won’t be added or removed for those elements.

A little introduction to event delegation

Event delegation consists of binding once, at the start, and passively listening for events to be triggered. It relies on the fact that many events in the browser bubble up.

As an example, after you click a <div>, its parent node receives the click event as well, and then it passes to the parent’s parent and so on, until it reaches the document element.

Pros and cons of each approach

Rebinding

Rebinding is simple: you just re-add the event handlers. It leads to new problems, such as adding event handlers to elements that were already bound. Some add CSS classes to work around this problem (marking those bound with a certain class).

All this requires CPU cycles every time the elements are updated and requires more and more event handlers to be created.

One way to work around both problems mentioned is to use named functions as event handlers. If you always use the same function, then you’ve solved the duplication problem, and the overhead is smaller.

Still, rebinding can lead to higher and higher amounts of RAM taken as time passes by.

Event delegation

Event delegation just requires an initial binding and there’s no need to deal with rebinding at all. This is quite a relief for the developer and makes the code shorter and clearer. The RAM problem mentioned before doesn’t apply to event delegation. The content of the page might change, but the active event handlers are always the same.

Event delegation has a catch, though. In order for it to work, the code that handles it (live(), a plugin or your own code) must take the element that got the event (event.target) and go through its ancestors to see which ones have event handlers to trigger along with some more processing. This means that, while event delegation requires less binding, it requires more processing each time an event is triggered.

Also, event delegation cannot be used with events that don’t bubble, such as focus and blur. For these events, there’s a workaround that works cross-browser, using the focusin and focusout events in some browsers.

Conclusion

Event delegation seems like a nicer approach, but it requires extra processing.

My advice on this matter is to use live bindings just when you really need them. These are two common situations:

Dynamic elements

You have a list of DOM elements that changes dynamically.

Large lists

Event delegation can work faster when you bind one live binding instead of, say, 100 from the regular ones. This is faster at the start and takes less memory.

If there’s no reason to use live(), then just go for bind(). If you then need to make it live, switching should be just a matter of seconds.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.152.58