I’ve had a somewhat extensive experience with jQuery so far. I’ve had the pleasure of reviewing peer jQuery code, mentoring potential web devs, teaching in classroom settings, and bashing heads over on StackOverflow, but one thing I’ve noticed all this time is just how hard it is to make developers do one simple thing in jQuery.
And when I say hard, I mean hard. Convincing jQuery developers to use delegate more in their code has got to be one of the more resilient challenges for someone teaching the jQuery craft.
The problem with the learning part of this (what I think at least) is that for devs coming into jQuery, you see bind() and then you see live(), and immediately think that those’re all you’ll need for working with events. Think about it: how long did it take you to know about delegate() from the time you learned about live()?
It’s easy to get lost when working with events in jQuery, even for just the somewhat harmless reason that everything is just so easy to do. For example, consider this:
jQuery(function($){// we're assigning an event handler to each li element
$('li').on('click',function(){
$(this).toggleClass('selected');});});
One would write this code and probably won’t give it a second look. After all, we’re writing our jQuery in pretty much a good form.
What most don’t realize, however, is that to some degree, this comes with it’s inefficiencies, particularly in the fact that binding an event listener in this way automagically attaches a distinct instance of the handler function to each distinct element in the jQuery wrapped set. That means that, for this particular example, we’re creating five references to a function, because each of the li elements gets its own piece of the pie.
If you’re interested in optimizing your code further, you could do this instead:
jQuery(function($){// we're delegating the event handler here
$('ul').on('click','li',function(){
$(this).toggleClass('selected');});});
What we’re doing here instead is that we’re assigning one (just one!) reference of the function to the ul element, and we’re telling it to fire it whenever a click occurs in any li inside it (hence, delegating).
Now, at first glance, that may not be much, but you’ll be surprised at how fast the performance hits can add up, to the point that there are arguments that delegate may actually be even more efficient to use in a majority of applications than bind.
It’s a pretty simple thing to do in your jQuery code (now even more so because of the unified on() function), so do keep this in mind whenever you’re writing to optimize your jQuery performance.
The latest version of jQuery (that is, 1.7.1, or 1.7.x) has been around for a good while already, and the new .on() and .off() functions still come to me as the shiniest new toys that came along with it. Not only are they shorter, more intuitive and better at what they do, but they also promote (generally) accepted jQuery good practice that has been around for quite a while.
What am I talking about?
If you’re any jQuery programmer worth your salt, then you know of, at the very least, .bind() and .live() when it comes to DOM event handling. The main difference being that the former is preferred for present elements, and the latter is “mandatory” for future element handling.
It’s admittedly a pet peeve of mine. And I’d like to think that the reason why .live() persists so sturdily when .delegate() is most clearly a better choice (aside from the fact that the latter was introduced one major version step later in the jQuery lifetime), is that .live()is so much easier to type.
Isn’t it? I mean look at it.
// live
$('a').live('click', do_backflips);// delegate
$(document).delegate('a','click', do_backflips);
Sheer convenience.
.on() and .off()
Which is a big reason why I -heart heart heart- the new .on() and .off() functions in jQuery 1.7.x. It streamlines all your event handling into just two functions, whether or not you’re taking into account present or future elements.
If you’re not familiar, the usage for .on() is like this:
// for present elements
$('a').on('click', do_backflips);// for future elements
$('#container').on('click','a', do_backflips);
I’d suggest you also take a look at the official documentation for the on and off functions as well. Pretty spiffy.
What’s really nice is that both versions of .on() map to .bind() and .delegate() respectively, which has the added benefit of simplifying the question of what function to use on any given case, and cleaning up syntax. And, hey, it’s also easy and intuitive to use.
Additionally, .live() has also been deprecated as of jQuery 1.7.x. I quote:
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers. Users of older versions of jQuery should use .delegate() in preference to .live().
Frankly, one could argue that good practice is being forced down the programmers’ throats. But as it plays on clear improvements on jQuery’s event model, I don’t mind that one bit.
One of the ways you can keep readers engaged to your website content is to make sure that links to external sites open to new tabs (or windows). This way, the material they’re engaged in on your site is not erased, they can switch to the newly opened content, and eventually go back to your content (and hopefully continue what they were doing).
However, as websites grow and scale, it can get a little cumbersome to keep track of all the links and manually mark each of them whether or not they open in a new tab. As a case in point, in WordPress (where it’s supposedly pretty easy to do this already), whenever you add a new hyperlink while writing a new article, you have to tick a checkbox to make it open a new tab.
Wouldn’t it be nice to automatically have links open in a new tab (or don’t)? With a bit of jQuery code, we can.
The Code
If you’re itching to jump straight to the code, then here it is, in all it’s -um- glory. Just copy and paste this into your jQuery-enabled page anywhere in between script tags.
Make sure you substitute your own domain name for the richardneililagan.com in there, and you’re all good to go.
If you want to know how that works, read on.
jQuery Magic
What we want to achieve is that when a link pointing outside of our domain is clicked, the linked content is loaded in a new tab. So it’s pretty clear that we want to filter all the links in our site for all those that point outside our domain.
We can do that using simple jQuery selectors:
// assuming that our domain is RICHARDNEILILAGAN.COMvar external_links =
$('a[href^="http://"]').not('[href*="richardneililagan.com"]');
The first selector (a[href^="http://"]) simply filters for all the links in our page with href values (the URL it links to) that start with http://. External links, unlike internal ones, require http:// for them to properly work, so it’s a good filter for our purposes.
The second filter .not('[href*="richardneililagan.com]') further filters our links by removing those links that have richardneililagan.com (our domain) in them. We’re assuming here that when a link points to something with richardneililagan.com in it, then its internal.
Now, we actually move to making these links open in new tabs. In semantic HTML, to make a link open in a new window/tab, you just have to give it the target = '_blank' attribute pair. So we’re going to do just that, but by using jQuery to do it for us.
external_links.attr('target','_blank');
With that, all our external links should have been updated target attributes so that they open in new tabs.
One of the questions I normally get from jQuery learners (aside from why the hell .bind() doesn’t work on dynamically-added HTML elements) is how to write a jQuery plugin. So for this article, I thought I’d introduce a fairly simple step-by-step walkthrough of creating a jQuery plugin that allows us to make something like this really quickly:
Doing this using vanilla jQuery
Of course, you can pretty much do this just by using plain jQuery, just as you would do with most other jQuery functionality. Something like the following would suffice:
// yeap, I know that this code can be optimized a big deal.
jQuery(function($){// we create two DIVs which form the two parts of our message boxvar $header = $('<div/>').addClass('header').text('Some header'),
$message = $('<div/>').addClass('message').text('Some message');// we add the click handler that makes the message slide up and down
$header.click(function(){ $message.slideToggle('fast');});// finally, we stick those two DIVs inside some other element
$('#foo').append($header).append($message);});
With something like that, you’ve pretty much managed to accomplish the same thing.
However, if we want to be able to reuse that piece of code on several other elements, it’d be a better idea to write out that whole jQuery logic into a plugin, so we can just call on it whenever we want.
The basics of extending jQuery
jQuery exposes two kinds of ways to extend it: one using the $ namespace, and the other using $.fn.
Extending the plain $ object allows you to define new utility functions. Adding a new utility function is as easy as just naming a new property:
// creating the "say" utility function
$.say=function(message){alert(message);};
We can then use that much like any other function by calling $.say('Bros before hoes.');, and it’ll behave as you’d expect it to.
An arguably more powerful way of extending jQuery is extending $.fn, which allows you to create new functions that act on wrapped objects (like, for example, .addClass()). You create new wrapper methods in much the same way you create utility functions:
Inside a new wrapper function, the this object is set to the jQuery wrapped set that the method was called on. So if $('#foo').foobar(); is called, then this inside foobar() is set to $('#foo'). This way, you can manipulate the elements as you see fit.
One of the things you should make a mental note of is to return the this context after the function resolves, so that your new wrapper method is compatible with jQuery chaining. (That is, you can do something like $('#foo').foobar().addClass('bar');.)
Changing our original jQuery code into a plugin
Now that we’ve got the basics out of the way, it should be fairly straightforward to rewrite our code above into a bit of jQuery plugin functionality. Calling our new method slidingBox() and applying the necessary changes in code, we get something like:
With that out of the way, we can then use that just by calling something like $('#foo').slidingBox('This is my header.','This is my message.');. With a bit of CSS styling, you’re more or less ready to go!
If you want a more interactive way of demo-ing the code discussed in this article, or if you want to tinker around with ready-made code a bit, then feel free to wreak havoc on a jsFiddle I prepared especially for this article. It’s got ready jQuery code, and some CSS to boot.
It’s not unusual to cache reusable components in your pages onto variables so that you don’t have to reselect / reevaluate them every single time you have to do something with them.
Let’s say I had a DIV element that I can append paragraphs onto. (Every) Javascript developer worth his/her salt performs some bit of namespacing to the effect of:
<divid="foo"><!-- BLAH --></div>
var app ={
foo : $('#foo')};
This way, whenever I’d want to reference that DIV again, I’ll just have to call it via app.foo. I’m actively refraining from polluting the global namespace with a slew of variables for all the elements I want cached, and I have a ready jQuery element that doesn’t need re-selection every time I want to use it.
Adding in functionality
Now, say that I want to add in the functionality to actually add in the paragraphs into my DIV. I can achieve that by doing something like:
var addParagraph =function(text){var myParagraph = $('<p></p>').text(text);
app.foo.append(myParagraph);};
… which is perfectly valid since app.foo is, in itself, a valid jQuery object. Improving on that a bit, I’d personally stick that function into the namespace I created prior, so that I don’t add unnecessary global objects.
That’s good. But if you’re a stickler for code (as I am), you’ll probably be itching to structure the code so that app.foo owns the addParagraph function. It makes better sense to have a function that acts on an object alone to be within scope of said object. I’ve got you.
… and that on its own isn’t exactly bad, as you’re getting exactly what you want done. However, the way I’d recommend it most of the time is by using jQuery’s $.extend() function.
$.extend() isn’t something that a lot of jQuery programmers bother with (at least, I think so), but it accomplishes more or less exactly the same thing. So why, then, should I use the $.extend()?
It’s cleaner and easier to add in multiple functionalities at once.
If I wanted to add in more stuff, like add, edit and remove paragraph functionalities into my object, my code would most probably end up a mess. Not that it’s not possible, but it’s a bit of a maintainance nightmare to have to code something like
app.foo.add=function(){};
app.foo.edit=function(){};
app.foo.remove=function(){};// ... some more functions
Wouldn’t you agree that that’s much easier to look at and take care of?
It’s easier to work with multiple components that share the same functionality.
Not only is it easier to slap on multiple functionalities on one object, but it’s also easier to slap on the same functionalities onto multiple objects.
If I had two of those DIVs, then traditionally I’d have to do something like this:
var app ={
foo : $('#foo'),
bar : $('#bar')};
app.foo.add=function(){};
app.foo.edit=function(){};
app.foo.remove=function(){};
app.bar.add=function(){};
app.bar.edit=function(){};
app.bar.remove=function(){};// yeap, I know that that's kind of a stretch
A more convenient method would be to cache a JSON object with the common functionalities, and use $.extend to slap those onto the relevant components.
Of course, you could also utilize Javascript prototyping at a lower level to achieve the same thing, but this is a somewhat quicker (and less complex) way of doing that.
You can check out a very rudimentary example of making it all work over on jsFiddle.
Deferred objects are a new addition to jQuery 1.5, and are meant to strike a cleaner division between executing a task and waiting for the task to complete (and reacting accordingly). In this article, we’ll talk about what deferred objects are and what they can do for you, and finish everything off with a simple example application.
If you’ve worked with Javascript a fair deal, then it’s probable that you’ve run across callback functions before. These are most prevalent in asynchronous operations (AJAX comes to mind, of course), and basically provide a way to execute something when an asynchronous operation completes (e.g. react accordingly when an AJAX operation succeeds or fails).
Let’s take a vanilla pre-jQuery 1.5 AJAX request as an example:
$.ajax({
url :'ajaxservice.svc/method',
data :'{}',
success :function(){alert('YAY!');},
error :function(){alert('gulp!');}});
Now, with the advent of deferred objects in jQuery 1.5, all $.ajax() derivatives now return deferred objects, which allow you to do the following:
var $defer = $.ajax({
url :'ajaxservice.svc/method',
data :'{}'});
$defer.success(function(){alert('YAY!');})
.error(function(){alert('gulp!');});
“So what?”, you may say. Well, if you haven’t noticed, the syntax on registering function callbacks is now more akin to the standard way of registering handlers in jQuery, such as with .click() or even .bind() and .delegate(). This allows us to chain registrations into the more familiar way of doing it in jQuery.
var $defer = $.ajax({
url :'ajaxservice.svc/method',
data :'{}'}).success(function(){alert('YAY!');})
.error(function(){alert('gulp!');});
$defer.success(function(){/* perform additional operations */});
The less-noticed advantage of this syntax (and, in my opinion, it’s greatest boon) is how it enables us to easily slap on multiple handlers onto the task’s result events, just like with the vanilla jQuery events such as .click(). The example above shows just that: we registered two separate function literals as two separate handlers for the success callback of a single AJAX operation.
// remember why we stopped doing this in the first place?<a onclick="alert('DOM Level 0 click handler!');" href="#">Argh!</a>
The old JSON parameter syntax of registering handlers on AJAX calls only allowed us to slap on one callback per event per call. Of course, we could go and create a function literal that calls all the necessary function handlers, but as systems scale and grow more complex, it gets harder and harder to maintain and keep track of everything that’s expected to happen when something happens.
Taking stuff one notch higher
While deferred objects make it so much easier to attach multiple event handlers on a single asynchronous call, that’s easily eclipsed by the fact that deferred objects also allow you to do the exact opposite: performing a task once several tasks are completed.
Let’s tackle this with a more real-world example. Imagine that we had a web page that makes two AJAX calls: one to get data from the server, and another to get an HTML template markup snippet. What we aim to do is that when the two AJAX calls resolve, we want to get our HTML template, slap the fetched content somewhere in it, and append the result into the current page’s DOM.
var flags =[];var my_handler =function(){if(flags.length==2){// do something with the data and HTML template in flags}};
$.ajax({
url :'ajax.svc/getdata',
success :function(m){
flags['data']= m.d;
my_handler();}});
$.ajax({
url :'ajax.svc/gettemplate',
success :function(m){
flags['template']= m.d;
my_handler();}});
The code above definitely has a number of improvement points going for it, but it effectively illustrates how we may go about with doing the business case we raised. While the code is easily readable and effectively clear, it suffers from the same problems that scalable applications aim to eliminate: as the code expands and the system grows more complex, we’re looking at maintaining larger and larger blocks similar to that above, and eventually that will just be too difficult to maintain and someone will likely trip up.
Along with deferred objects, jQuery also exposes the new $.when() utility function that takes care of managing tasks similar to the one above.
$.when(
$.ajax({ url :'ajax.svc/getdata'}),
$.ajax({ url :'ajax.svc/gettemplate'})).then(function(data, template){// do something with the data and template});
$.when() accepts any number of deferred objects (which $.ajax() derivatives now return), and automatically attach event handlers to the tasks as a singular collective. The resulting data from each deferred object are mapped to the handler function’s parameters in the same order as they were declared (i.e. the resulting data from $.ajax({ url : 'ajax.svc/getdata' }) is mapped to data in the function literal in the .then() call, and so on).
This construct accepts three primary handlers:
.done() – called when all deferred tasks succeed
.fail() – called when at least one of the deferred tasks fail
.complete() – called as soon as all deferred tasks complete, whether or not they succeeded or not
The .then() is shorthand for declaring both .done() and .fail() : the first parameter takes the on-success callback, while the second takes the on-fail callback.
Creating a deferred object
While $.ajax() derivatives now automatically return a deferred object, you may want to manually create deferred objects in your code for specific purposes.
To create a deferred object, you simply call $.Deferred(). These objects basically have three states: unresolved, resolved and rejected.
Deferred objects start off as unresolved, and are either resolved or rejected based on a task’s outcome. When manually handling deferred objects, you’d want to either call .resolve() or .reject() based on a task’s outcome.
Let’s build our own deferred logic example
To put everything we’ve discussed together, we’ll try to create a very simple system that makes use of deferreds.
We’ll use an application that has three INPUT fields. The user is expected to fill up all three fields, so we want a task to run when all three fields have been completed. For this example, we’re going to use deferred objects to update a simple message when all three fields have been filled up by the user.
To start off, here is our completed application:
Form is incomplete.
First Name
Second Name
Third Name
Let’s get started!
We’ve got three INPUT fields that we want filled, so it makes sense to create three separate deferred objects; one for each field.
var $deferreds =[];// our INPUTs are inside a DIV#example-pane, so we set the context to that
$('input','#example-pane').each(function(i,e){
$deferreds[i]= $.Deferred();});
We then initialize the task to perform once all our deferred objects resolve. In this case, we just want to change the text in a simple SPAN element.
// we use the JS .apply() method to call the $.when() function with the elements// of an array laid out as the function arguments
$.when.apply(null, $deferreds).then(function(){
$('#example-pane span').text('complete').css('color','#6d6');});
Finally, we need to manually resolve our deferred objects at the proper time. In our example, we want to resolve the corresponding deferred object when the value of an INPUT field is changed.
Our example can use some improvements (like caching $('#example-pane') for example), but hopefully it has been a clear illustration of how jQuery deferred objects can work with you in your code.
Tech+Life+Music is a technology / programming blog authored by Richard Neil Ilagan.
We learn something new everyday, and this serves a bit like a repository of the interesting stuff, challenges and boss fights I come across in my work and play. I blog mostly about progressive front-end development in .NET, jQuery and generic programming for the web.
Follow me on @techlifemusic, or add me up on Facebook and/or Google.
Discussion