Before we dive into the ins and outs of these methods, let's start with
some common HTML markup that we'll be using as we write sample jQuery
code.
<ul id="members" data-role="listview" data-filter="true">
<!-- ... more list items ... -->
<li>
<a href="detail.html?id=10">
<h3>John Resig</h3>
<p><strong>jQuery Core Lead</strong></p>
<p>Boston, United States</p>
</a>
</li>
<!-- ... more list items ... -->
</ul>
Using the Bind Method
The .bind()
method registers the type of event and an event
handler directly to the DOM element in question. This method has been
around the longest and in its day it was a nice abstraction around the
various cross-browser issues that existed. This method is still very
handy when wiring-up event handlers, but there are various performance
concerns as are listed below.
/* The .bind() method attaches the event handler directly to the DOM
element in question ( "#members li a" ). The .click() method is
just a shorthand way to write the .bind() method. */
$( "#members li a" ).bind( "click", function( e ) {} );
$( "#members li a" ).click( function( e ) {} );
The .bind()
method will attach the event handler to all of
the anchors that are matched! That is not good. Not only is that
expensive to implicitly iterate over all of those items to attach an
event handler, but it is also wasteful since it is the same event
handler over and over again.
Pros
- This methods works across various browser implementations.
- It is pretty easy and quick to wire-up event handlers.
- The shorthand methods (
.click()
, .hover()
, etc...) make it even easier to wire-up event handlers.
- For a simple ID selector, using
.bind()
not only wires-up quickly, but also when the event fires the event handler is invoked almost immediately.
Cons
- The method attaches the same event handler to every matched element in the selection.
- It doesn't work for elements added dynamically that matches the same selector.
- There are performance concerns when dealing with a large selection.
- The attachment is done upfront which can have performance issues on page load.
Using the Live Method
The .live()
method uses the concept of event delegation to perform its so called "magic". The way you call .live()
looks just like how you might call .bind()
, which is very convenient. However, under the covers this method works much different. The .live
method attaches the event handler to the root level document along with
the associated selector and event information. By registering this
information on the document it allows one event handler to be used for
all events that have bubbled (a.k.a. delegated, propagated) up to it.
Once an event has bubbled up to the document jQuery looks at the
selector/event metadata to determine which handler it should invoke, if
any. This extra work has some impact on performance at the point of user
interaction, but the initial register process is fairly speedy.
/* The .live() method attaches the event handler to the root level
document along with the associated selector and event information
( "#members li a" & "click" ) */
$( "#members li a" ).live( "click", function( e ) {} );
The good thing about this code as compared to the .bind()
example above is that it is only attaching the event handler once to the
document instead of multiple times. This not only is faster, but less
wasteful, however, there are many problems with using this method and
they are outlined below.
Pros
- There is only one event handler registered instead of the numerous event handlers that could have been registered with the
.bind()
method.
- The upgrade path from
.bind()
to .live()
is very small. All you have to do is replace "bind" to "live".
- Elements dynamically added to the DOM that match the selector
magically work because the real information was registered on the
document.
- You can wire-up event handlers before the document ready event helping you utilize possibly unused time.
Cons
- This method is deprecated as of jQuery 1.7 and you should start phasing out its use in your code.
- Chaining is not properly supported using this method.
- The selection that is made is basically thrown away since it is only used to register the event handler on the document.
- Using event.stopPropagation() is no longer helpful because the event has already delegated all the way up to the document.
- Since all selector/event information is attached to the document
once an event does occur jQuery has match through its large metadata
store using the
matchesSelector
method to determine which event handler to invoke, if any.
- Your events always delegate all the way up to the document. This can affect performance if your DOM is deep.
Using the Delegate Method
The .delegate()
method behaves in a similar fashion to the .live()
method, but instead of attaching the selector/event information to the
document, you can choose where it is anchored. Just like the .live()
method, this technique uses event delegation to work correctly.
If you skipped over the explanation of the .live()
method you might want to go back up and read it as I described some of the internal logic that happen.
/* The .delegate() method behaves in a similar fashion to the .live()
method, but instead of attaching the event handler to the document,
you can choose where it is anchored ( "#members" ). The selector
and event information ( "li a" & "click" ) will be attached to the
"#members" element. */
$( "#members" ).delegate( "li a", "click", function( e ) {} );
The .delegate()
method is very powerful. The above code
will attach the event handler to the unordered list ("#members") along
with the selector/event information. This is much more efficient than
the .live()
method that always attaches the information to
the document. In addition a lot of other problematic issues were
resolved by introducing the .delegate()
method. See the following outline for a detailed list.
Pros
- You have the option of choosing where to attach the selector/event information.
- The selection isn't actually performed up front, but is only used to register onto the root element.
- Chaining is supported correctly.
- jQuery still needs to iterate over the selector/event data to
determine a match, but since you can choose where the root is the amount
of data to sort through can be much smaller.
- Since this technique uses event delegation, it can work with dynamically added elements to the DOM where the selectors match.
- As long as you delegate against the document you can also wire-up event handlers before the document ready event.
Cons
- Changing from a
.bind()
to a .delegate()
method isn't as straight forward.
- There is still the concern of jQuery having to figure out, using the
matchesSelector
method, which event handler to invoke based on the selector/event
information stored at the root element. However, the metadata stored at
the root element should be considerably smaller compared to using the .live()
method.
Using the On Method
Did you know that the jQuery .bind()
, .live()
, and .delegate()
methods are just one line pass throughs to the new jQuery 1.7 .on()
method? The same is true of the .unbind()
, .die()
, and .undelegate()
methods. The following code snippet is taken from the jQuery 1.7.1
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
unbind: function( types, fn ) {
return this.off( types, null, fn );
},
live: function( types, data, fn ) {
jQuery( this.context ).on( types, this.selector, data, fn );
return this;
},
die: function( types, fn ) {
jQuery( this.context ).off( types, this.selector || "**", fn );
return this;
},
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},
undelegate: function( selector, types, fn ) {
return arguments.length == 1 ?
this.off( selector, "**" ) :
this.off( types, selector, fn );
},
With that in mind, the usage of the new .on()
method looks something like the following...
/* The jQuery .bind(), .live(), and .delegate() methods are just one
line pass throughs to the new jQuery 1.7 .on() method */
// Bind
$( "#members li a" ).on( "click", function( e ) {} );
$( "#members li a" ).bind( "click", function( e ) {} );
// Live
$( document ).on( "click", "#members li a", function( e ) {} );
$( "#members li a" ).live( "click", function( e ) {} );
// Delegate
$( "#members" ).on( "click", "li a", function( e ) {} );
$( "#members" ).delegate( "li a", "click", function( e ) {} );
You'll notice that depending how I call the .on()
method changes how it performs. You can consider the .on()
method as being "overloaded" with different signatures, which in turn changes how the event binding is wired-up. The .on
method bring a lot of consistency to the API and hopefully makes things slightly less confusing.
Pros
- Brings uniformity to the various event binding methods.
- Simplifies the jQuery code base and removes one level of redirection since the
.bind()
, .live()
, and .delegate()
call this method under the covers.
- Still provides all the goodness of the
.delegate()
method, while still providing support for the .bind()
method if you need it.
Cons
- Brings confusion because the behavior changes based on how you call the method.
Conclusion (tl;dr)
If you have been confused about the various different types of event
binding methods then don't worry, there has been a lot of history and
evolvement in the API over time. There are many people that view these
methods as magic, but once you uncover some of how they work it will
help you understand how to better ode inside of your projects.
The biggest take aways from this article are that...
- Using the
.bind()
method is very costly as it attaches the same event handler to every item matched in your selector.
- You should stop using the
.live()
method as it is deprecated and has a lot of problems with it.
- The
.delegate()
method gives a lot of "bang for your buck" when dealing with performance and reacting to dynamically added elements.
- That the new
.on()
method is mostly syntax sugar that can mimic .bind()
, .live()
, or .delegate()
depending on how you call it.
- The new direction is to use the new
.on
method. Get familiar with the syntax and start using it on all your jQuery 1.7+ projects.
Were there any pros or cons that you would have added to the above lists? Have you found yourself using the .delegate
method more recently? What are you thoughts on the new .on
method? Leave your thoughts in the comments. Thanks!
No comments:
Post a Comment