Contents


Advanced jQuery

Turning a good app into a great app

Comments

This series on jQuery has taken you far in your abilities to create a JavaScript-based Web application. Even if you had never heard of jQuery before you started reading these articles, by now you should have all the skills and background in the language to produce a good Web application using jQuery. However, sometimes a good application is not enough. Sometimes you need a great Web application. This requires a few extra steps, steps you add to your application to ensure it runs smoothly in big apps and small apps and looks just right to every user. They are the last layers of polish you add to make your Web application sparkle.

In this article, I'll discuss performance-based improvements to your code, but I'll also talk about things in the jQuery library that some people may gloss over. These items are important in complex applications, they are plug-ins that are vital to anyone's applications, and they are nice design tips to make your Web apps easier to code. Finally, in the last section, I'll talk about some of the new features in jQuery 1.3, which was released recently and added a few new features to the library.

The first sample application

Most of the tips in this article can be found in the sample application attached to this article (see Downloadable resources), which is a straightforward e-mail Web application. It should look familiar as it's the same one that I presented in the first article in the series. However, you should see how far it has come from the first article, how the performance has been improved, and how the final layers of polish to it has turned it into a really great Web application.

Figure 1. The example application
Screen shot of the mail web application showing a typical mail box with a sidebar of Inbox and Sent, buttons at the top to Compose or Delete, and a list of the Inbox contents with clickable messages.
Screen shot of the mail web application showing a typical mail box with a sidebar of Inbox and Sent, buttons at the top to Compose or Delete, and a list of the Inbox contents with clickable messages.

Bind/Unbind

In the Events module, there are two functions, called bind() and unbind(), that appear on the surface to duplicate the efforts of all the other event methods. After all, if you can simply attach a click() method to a page element, why would you need to call bind("click") on it instead? It's just a waste of keystrokes. Well, it turns out that these functions come in handy in certain situations, and when used correctly they can drastically increase the performance of your application. These functions do give you the ability to attach events to certain page elements, just like the numerous event methods in the module, but they also allow you to remove these events from the page elements. Why would you want to do this? Well, let's take a look at the Web application and see how they can be used in this situation.

Figure 2. Bind/Unbind example
A diagram shows a portion of the Inbox message listing with notations on various elements.  The Select All/Deselect All checkbox is at the top left.  Individual checkboxes are identified running alongside each message row.  The message 'You have 5 selected messages' is updated on each checkbox selection, summing the total number of rows that have a checkbox selected.
A diagram shows a portion of the Inbox message listing with notations on various elements. The Select All/Deselect All checkbox is at the top left. Individual checkboxes are identified running alongside each message row. The message 'You have 5 selected messages' is updated on each checkbox selection, summing the total number of rows that have a checkbox selected.

Listing 1 shows the code to set this up, as it was originally coded, before I improved it:

Listing 1. Widget before optimization
$(document).ready(function(){
   // cache this query since it's a search by CLASS
   selectable = $(":checked.selectable");
   // when the select/deselect all is clicked, do this function
   $("#selectall").click(selectAll);
   // whenever any individual checkbox is checked, change the text
   // describing how many are checked
   selectable.click(changeNumFilters);
   // calculate how many are initially checked
   changeNumFilters();
});

var selectable;

function changeNumFilters()
{
   // this needs to be checked on every call
   // since the length can change with every click
   var size = $(":checked.selectable").length;
   if (size > 0)
      $("#selectedCount").html(size);
   else
      $("#selectedCount").html("0");
 }
 
// handles the select/deselect of all checkboxes
function selectAll()
{
   var checked = $("#selectall").attr("checked");
   selectable.each(function(){
      var subChecked = $(this).attr("checked");
      if (subChecked != checked)
      {
         $(this).click();
      }
   });
   changeNumFilters();
   }

This code should look relatively straightforward, as it's pretty much the same widget I've worked on in a few of the articles. You saw the "select/deselect all" widget in the first article where I showed it in its basic form. In the performance article, you saw how to improve its performance by caching your selection queries and minimizing your usage of searching by CLASS. However, there's still a problem. When the "select/deselect all" checkbox is pressed in a table with 100 rows, you see very poor performance. In fact, on my browser, with this code in place, the average time to complete the select all was 3.4 seconds. That's not very responsive at all. Even after all of the optimization to this point, you still have something that's not acceptable.

Let's go one step deeper in the algorithm here and see if something is amiss. You're looping through every checkbox on the page and seeing if it's current "checked" status is the same as the "select/deselect all" checkbox. If it's not, then you're calling "click" on it, to make it match the state of the "select/deselect all" checkbox. Wait a minute though... you also attached a function to these checkboxes, so that with each click, you're calling the changeNumFilters() function. Upon closer examination, you've set up an algorithm that calls the changeNumFilters() a possible 101 times. No wonder the performance was so bad. Obviously, you don't need to update the count of selected messages with each click, you can do it once at the end of the process. How do you prevent this method from getting called, while at the same time clicking the checkbox?

Well, of course, the unbind() method is where this comes into play. You probably saw this coming. By calling unbind() before the checkbox is clicked, it will stop the call to click() from in turn calling the changeNumFilter() method. This is great! This is what you wanted. Now it won't be called 101 times. But, of course, this would only work once, so you must attach the click method back to each checkbox after you're done calling click on it, using the bind method. See the updated version in Listing 2.

Listing 2. Widget after optimization
// handles the selection/unselection of all checkboxes
function selectAll()
{
   var checked = $("#selectall").attr("checked");
   selectable.unbind("click", changeNumFilters);
   selectable.each(function(){
      var subChecked = $(this).attr("checked");
      if (subChecked != checked)
      {
          $(this).click();
      }
   });
   selectable.bind("click", changeNumFilters);
   changeNumFilters();
   }

With these optimizations, you have gotten your checkbox to run in approximately 900ms, which is quite an improvement in performance. It was all from taking a step back and thinking about exactly what your algorithm was doing, and how your actions would trickle through the code. Instead of calling a function 100 times, you can simply call it once. Hooray! After looking at this function for this many articles, you have finally made it as fast and efficient as possible. Well, not really... It turns out there's a super-fast way of running this algorithm that I've been holding back from you until now. Besides, if I had given you the fast algorithm right away, I wouldn't have been able to use this function as a lesson for these articles. Hopefully, this situation has opened your eyes to the benefits of using the bind/unbind feature in your code (if there's no shortcut of course).

Remember: Use the bind/unbind in situations where you don't want the default event to fire, or as a way to temporarily attach or remove events from page elements

Listing 3 shows the super-fast way of writing this algorithm, if you have this widget in your own code. It runs this function in 40 ms, blazing fast compared to the other ones we've tried.

Listing 3. Widget with super-fast algorithm
function selectAll()
{
   var checked = $("#selectall").attr("checked");
   selectable.each(function(){
      $(this).attr("checked", checked);
   });
   changeNumFilters();
   }

Live/Die

Two of the great new additions to the 1.3 version of jQuery are the live() and die() functions. Their role in a well-designed Web application can best be seen through an example. Imagine attaching a double-click to every cell in a table. As an experienced jQuery veteran, you know you should create this setup in your document.ready() function, like shown in Listing 4.

Listing 4. Setting up a double-click
$("tr.messageRow").dblclick(function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
   }
   });

There's only one problem with this design. Yes, it attaches the double-click event to every row in the table with a class of messageRow. But, what if you add new rows to the table? For example, those rows might appear when you have an additional message that gets loaded into the page using Ajax without a page reload. This presents a problem, because the code, as written, won't work. The event you created was bound to all the existing tr.messageRow elements that were present when the page loaded. It's not bound to any new tr.messageRows you create during the page's lifetime. People who write code like this would be disappointed to discover that their code didn't work. Beginner jQuery programmers may even spend a few hours of headache-inducing debugging trying to figure out why their code isn't working, before stumbling upon this fact in the jQuery documentation. (Yes, that was me last year).

Before jQuery 1.3, there were three ways to get around this problem, none of them elegant (for those of you who continue to use jQuery 1.2.x, these are still valid). The first was the re-initialization technique, which simply reattached the event to the selected elements each time a new one was added. The second was taking advantage of the bind/unbind methods, which we saw in the previous section. See them both in Listing 5.

Listing 5. Work-around for attaching events to new elements
// first technique to deal with "hot" page elements, added during the page's
// lifetime
$("#mailtable tr #"+message.id).addClass("messageRow")
   .dblclick(function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
   }

// second technique to deal with "hot" page elements
$("#mailtable tr #"+message.id).addClass("messageRow")
   .bind("dblclick", (function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
      }

Neither of these techniques were very elegant though, as you can probably tell. You'd be repeating code. And, you'd be forced to find all the possible spots that a new page element could be added to the page and deal with the "hot element" issue at that point. That's not a good way of programming. Besides, this is jQuery. It's supposed to make everything incredibly easy for us. It's supposed to do everything for us.

Luckily, there was a plug-in that seemed to solve these problems. It's called the LiveQuery plug-in, and it allowed you to bind certain page elements to events, but to do so in a "live" fashion, so that all page elements, including ones that existed at page creation and ones created during the page's lifetime (through Ajax calls for example), would trigger the event to occur. This was a very clever and important plug-in for UI developers, as it made working with dynamic pages as easy as working with static ones. For a Web app developer, it was really one of those "must have" plug-ins.

The jQuery core team agreed so much in the importance of this plug-in that they incorporated it into their 1.3 release. This "live" feature is now a part of the core jQuery, so any developer can take advantage of the function. The plug-in is almost exactly duplicated in the 1.3 core code, minus a few events that didn't make the cut in the first release of 1.3. I'd bet some money that these missing events will appear in one of the next releases of jQuery. Let's take a look at how it can be used to change your code.

Listing 6. The "live" event model
$("tr.messageRow").live("dblclick", function() {
   if ($(this).hasClass("mail_unread"))
   {
      $(this).removeClass("mail_unread");
      }

Making this small change in the code, all tr.messageRow elements that are on the page will trigger this code when double-clicked. This is not the behavior you'd see if you simply used the dblclick() function, as I've explained above. For this reason, I highly recommend that you think about using the live() method for most of your event methods. In fact, I'd say it's a must that any page that creates page elements dynamically, whether through Ajax or user interaction, must use the live() function instead of the alternative event methods. It's just such a great trade-off between ease of coding versus. possible bugs, that it's a no-brainer decision.

Remember: Always use the live() method when attaching events to dynamic page elements. This makes your events as dynamic as your page elements.

Ajax Queue/Sync

The usage of Ajax calls to the server is becoming the measure by which Web 2.0 companies measure themselves. As we've discussed endlessly here, using Ajax in jQuery is as simple as calling a method call normally. This means you can call any server-side Ajax function as easily as you would a client-side JavaScript function. This bonus has some less than desirable side-effects though, and these can occur if there are too many Ajax calls to the server. If a Web application is going overboard with Ajax calls, problems can certainly arise.

The first problem is that some browsers limit the number of open connections to the server. In Internet Explorer, the current version allows only 2 open concurrent connections to the server. Firefox allows 8 connections, but it is still a limit on open connections. If a Web application is making Ajax calls out of control, it can certainly have more than 2 open connections, especially if the server-side call is time-intensive. This problem can arise from bad design from the Web app designer, or even a user who is out of control with their requests. In any case, it's not a good state to be in, and you don't want to be in any situation where the browser is deciding what connections it will allow and which ones it won't.

Further, because the calls are asyncronous (the "A" in Ajax), the returns from the server are not guaranteed to be in the same order you sent them. Wait....what? Yes, if you make 2 Ajax calls at about the same time, you aren't guaranteed that the responses from the server will come back in the same order. Thus, if your second call depends on your first one finishing first, you may be out of luck. Imagine a scenario where your first call retrieves data, and your second call manipulates the data in some way on the client-side. If your second call returns faster than your first Ajax call, your code may lead to errors. You have no way of guaranteeing the response speeds. Multiply this issue by 4, and you can see problems start to arise quickly.

The creator of jQuery recognized this as a potential problem, but also recognized that it was only going to be a problem for Web applications about 1% of the time. However, for those developers who are in the 1%, they need a solution. He created a plug-in that can be used to sort through this problem, by creating an Ajax Queue, and an Ajax Sync. Both are very similar in how they function: the Queue places Ajax calls one at a time, waiting for the other to return before starting the next one. The Sync sends them out immediately, but only returns to the calling function when the previous one has already returned.

This solves the overload problem by controlling the Ajax calls on the client side, and also controlling and regulating how the responses are sent back to the client-side code. You can now be sure you know the order in which responses come back to the client, enabling you to write code that can predict the order of events. Let's take a look at how this plug-in works, and how you can use it in your code with the example in Listing 7. Keep in mind that this is designed for the 1% of conditions where multiple Ajax calls have vital information dependent on previous Ajax calls. This example is not one of those examples, but it nevertheless lets you see how the plug-in can be used (it would be difficult to create a good real-world example where this plug-in is needed and still have it be easy to understand).

Listing 7. Ajax Queue
var newRow = "<tr id='?'>" +
             "<td><input type=checkbox value='?'></td>" +
             "<td>?</td>" +
             "<td>?</td>" +
             "<td>?</td>" +
             "<td>?</td></tr>";
   
   
$("#mailtable").everyTime(30000, "checkForMail", function(){

   // by using the Ajax Queue here, we can be sure that we will check for mail
   // every 30 seconds, but ONLY if the previous mail check has already returned.
   // This actually would be beneficial in a mail application, if one check for
   // new mail takes longer than 30 seconds to respond, we wouldn't want the
   // next Ajax call to kick off, because it might duplicate messages (depending
   // on the server side code).
   // So, by using the Ajax Queue plug-in, we can ensure that our Web client
   // is only checking for new mail once, and will never overlap itself.

    $.ajaxQueue({
         url: "check_for_mail.jsp",
         success: function(data)
         {
           var message = eval('(' + data + ')');
           if (message.id != 0)
           {
             var row = newRow.replace("?", message.id);
             row = row.replace("?", message.id);
             row = row.replace("?", message.to);
             row = row.replace("?", message.from);
             row = row.replace("?", message.subject);
             row = row.replace("?", message.sentTime);
             $("#mailtable tbody").prepend(row);
             $("#mailtable #"+message.id).addClass("mail_unread").addClass("messageRow");
             $("#mailtable #"+message.id+ " td").addClass("mail");
             $("#mailtable :checkbox").addClass("selectable");
            }
          }
          });

Remember: If your application has multiple Ajax calls that can overlap each others' functionality, look to use an Ajax Queue or Ajax Sync.

Second example Web application

I will use another widget to address the final 3 things in this article, and I want to show it off and explain it before I delve into the code behind it. It's the familiar 401k widget, the one you saw in one of the previous articles (see Related topics for links to the articles). However, there is a subtle difference this time around, because I've included the widget twice on the same page. It's attached to two separate tables. That brings up several interesting points. Figure 3 shows what the widget looks like:

Figure 3. 401k widget
A screenshot shows two versions of the 401k widget with the instructions 'Enter the percentage allocation you wish to place YOUR OWN 401k contributions into.'  Below that are a number of rows bearing different investment options and a text entry box followed by a percent sign.  At the bottom is a row showing Total and the sum of all the text boxes.  The top example shows various numeric entries with the bottom total of 64 in red, since it should add to 100.  The bottom example shows that the values have changed and that the rows have automatically resorted themselves high-to-low based on the numeric entry with a new total of 65, still in red.
A screenshot shows two versions of the 401k widget with the instructions 'Enter the percentage allocation you wish to place YOUR OWN 401k contributions into.' Below that are a number of rows bearing different investment options and a text entry box followed by a percent sign. At the bottom is a row showing Total and the sum of all the text boxes. The top example shows various numeric entries with the bottom total of 64 in red, since it should add to 100. The bottom example shows that the values have changed and that the rows have automatically resorted themselves high-to-low based on the numeric entry with a new total of 65, still in red.

In this widget, I am doing a few things. The first is totaling the textfields and determining if they collectively add up to 100. If they don't add up to 100, I display an error indication to the user that they haven't used the widget correctly. Second, I sort each of the choices after each choice receives input. In this manner, the investment allocation with the highest percentage will always "percolate" to the top of the table. You can see this in Figure 3, which shows the choices sorted numerically by percentage. Finally, to make it look cool, I'll add some stripes to it.

The HTML code to produce this widget is surprisingly spartan. Listing 8 shows it is in its entirety.

Listing 8. HTML for 401k widget
<p><table width=300 class="percentSort" cellpadding=0 cellspacing=0>
<tbody>
  <tr><td>S&P 500 Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>Russell 2000 Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>MSCI International Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>MSCI Emerging Market Index</td>
  <td><input type=text> %</td></tr>
  <tr><td>REIT Index</td>
  <td><input type=text> %</td></tr>
</tbody>
<tfoot>
</tfoot>
</table>

Setting up widgets in jQuery

That little bit of HTML leads directly into this section, which centers on the idea of setting up widgets, and all the code that they need, within the jQuery code itself. You're obviously used to doing this when you attach events to things, and for simple things like attaching classes in certain situations. For some, though, this is taking it a step further. All of the setup code for these widgets is going to be in the jQuery code itself. I can give you all the usual mumbo-jumbo about separation of roles, letting the HTML designer do his work and the JavaScript coder do his, but you've all heard that a million times already. I'll just add one other thing here, and that's the idea of "class decoration," which so many plug-in authors have taken to using as well. Looking at the HTML code in Listing 8, by simply attaching a percentSort class to the table, you can transform the entire functionality and look of the table. This is really the goal of any widget you design, to make the addition and removal of the widget as simple as adding a class to it.

Let's go through several of the steps I've used to setting up the widgets in jQuery. By looking through these steps, you can begin to see how this design pattern emerges in Listing 9.

Listing 9. jQuery widget code
$(document).ready(function() {

  // the first step is to find all the tables on the page with
  // a class of percentSort.  These are all the tables we want to
  // convert into our widget.
  // After we find them, we need to loop through them and take some
  // actions on them
  // At the conclusion of this block of code, each table that's going to
  // be a percentSort widget will have been transformed

  $("table.percentSort").each(function(i){

     // each table needs a unique ID, for namespace issues (discussed later)
     // we can simply create a uniqueID from the loop counter

     $(this).attr("id", "percentSort-"+i);

     // within each table, let's highlight every other row in the table, to
     // give it that "zebra" look

     $(this).find("tbody > tr").filter(":odd").addClass("highlight");

     // because each table needs to show the "Total" to the user, let's create a new
     // section of the table in the footer.  We'll add a row in the table footer
     // to display the words "Total" and a span for the updated count.

     $("#"+$(this).attr("id") + " tfoot")
          .append("<tr><td>Total</td><td>
          <span></span> %</td></tr>");

     // finally, let's add the CLASS of "percentTotal" to the span we just
     // created above.  We'll use this information later to display
     // the updated totals

     $("#"+$(this).attr("id") + " tfoot span").addClass("percentTotal");
  });

  // now the second step, after we've completed setting up the tables themselves
  // is to set up the individual table rows.
  // We can similarly sort through each of them, taking the appropriate actions
  // on each of them in turn.
  // Upon completion of this block of code, each row in each table will be
  // transformed for our widget

  $("table.percentSort tbody > tr").each(function(i){

     // get the namespace (to be discussed in the next section)

     var NAMESPACE = $(this).parents("table.percentSort").attr("id");

     // attach a unique ID to this row.  We can use the loop counter
     // to ensure the ID is unique on the page (which is a must on every page)

     $(this).attr("id", "row"+i);

     // now, within this row of the table, we need to find the text input, because
     // we need to attach a class to them.  We utilize the namespace, and also
     // find the :text within the table row, and then attach the correct class

     $("#"+$(this).attr("id") + " :text").addClass("percent");

     // Finally, we attach a unique ID to each of the text inputs, and we do this by
     // making it a combination of the table name and the row name.

     $("#"+$(this).attr("id") + " .percent").
               attr("id", NAMESPACE + "-" + $(this).attr("id"));
  });


  // Finally, because we know we only want numerical inputs, we restrict the text entry
  // to just numbers.  We must do this now, because up until this point, the page
  // contained no elements with the CLASS "percent"
  $(".percent").numeric();

As you can see from this example, you can really introduce a lot of functionality into your HTML code in the jQuery code. The benefits to this type of design should be obvious, and follow along the lines of separation of roles, reuse of code, and so on. You'll see this type of design in widget plug-ins as well, as they take your seemingly simple HTML markup and turn it into whatever widget the plug-in is designed for. Essentially, that's what you're doing here as well, writing a plug-in to turn a simple table into a sorting and summation table.

Remember: Set up as much of the code in the jQuery code itself, and use as little HTML as you can.

Namespace

Perhaps one of the most confusing aspects of working with this type of design, and with jQuery itself, is to properly understand the namespace in which the code is working. That's why this example is so good, because it presents this problem in a very visible way. Writing jQuery code when you know every ID and class on the page can be very straightforward and easy. That's what it was designed for after all. What happens when you aren't sure how many classes are on the page, or they start to overlap themselves? In this example, you can see this issue right away. There are two of the exact same widgets! Everything is overlapped. It looks like what happens when you don't take namespace issues into account; your widgets start to interfere with each other and they end up not working properly at all.

Figure 4. Ignoring namespace issues
A screenshot shows two versions of the 401k widget with the instructions 'Enter the percentage allocation you wish to place YOUR OWN 401k contributions into.'  Below that are a number of rows bearing different investment options and a text entry box followed by a percent sign.  At the bottom is a row showing Total and the sum of all the text boxes.  The top example shows two numeric entries with the bottom total of 22 in red, since it should add to 100.  The bottom example shows all text boxes clear, but the total still shows 22, still in red.
A screenshot shows two versions of the 401k widget with the instructions 'Enter the percentage allocation you wish to place YOUR OWN 401k contributions into.' Below that are a number of rows bearing different investment options and a text entry box followed by a percent sign. At the bottom is a row showing Total and the sum of all the text boxes. The top example shows two numeric entries with the bottom total of 22 in red, since it should add to 100. The bottom example shows all text boxes clear, but the total still shows 22, still in red.

This happens because you are ignoring which widget specifically should be updated in your bad code, simply calling things like $(".percentTotal"). Because you have multiple tables on the same page, there are going to be multiple instances of the percentTotal class on the page. Yes, if you only had 1 of these tables on a page, you could take for granted that this would exist only once. But as pages become more advanced, and widgets become more reused, that assumption should be tossed out the window. Some people may ask, "Why not just use an ID here instead?" That wouldn't solve the issue: after all, what ID would you give it? You can't use "percentTotal" because that would create ambiguity. You can't use "percentTotal-1" because that doesn't really mean anything on the page. (The numbers are just arbitrarily created by you, after all.) You could attach some reference to the table it's contained in, something like "percentTotal-percentSort1," which would be one solution, but this is overly complicating things. jQuery has a super-sophisticated and very easy to use selection syntax, making these types of hybrid naming schemes unnecessary. Why recreate the wheel here? Let's use jQuery's selection engine to solve your namespace issues for you.

At the core of the problem in this widget, and in general, is determining which widget the action took place in. Ask yourselves when you enter a number in one of the textfields, how does jQuery know which textfield the text was entered in? You attached an event to the class of "percent" in the code of course, and you can reference it with the $(this) inside your code. That brings us to the next question: How does jQuery know which widget this occurred in, so you can update the appropriate percentTotal field? Ummm.....I guess it doesn't, at least not easily. That's right. While your code is elegant, in that you can attach an event to every textfield with a class of "percent" on the page, it is inelegant if you simply ignore the containing widget in which the event occurred.

The reason this problem is termed a namespace problem is because you are being ambiguous with the naming of the widgets, which can lead to problems. For the jQuery code to work properly, each name needs to be clearly defined in its own space, hence the term namespace. You must write code that avoids overlapping names, so that each widget can be self-contained. You should be able to add multiple instances of the same widget on the same page without any of the names overlapping. Essentially, each widget must stand alone on a page.

There is no one right way to deal with this namespace issue, so I'll present my solution, which you can use in your own code, or you can read about the problem and create your own better solution. The reason I like this code is because it is simple to use (only 1 line), and gives you some control over your own namespace. Take a look in Listing 10.

Listing 10. Namespace solution
// our event is attached to EVERY input text field with a CLASS of "percent"
// this makes our code look good, but can lead to namespace issues
$("table.percentSort input.percent").keyup(function(){

   // this simple line can establish a namespace for us, by getting the unique
   // ID attached to our table (the table is considered the "container" for
   // our widget.  It could be anything, depending on your specific code.)
   // We pass the CLASS of our widget to the parents() function, because
   // that effectively encapsulates the widget

   var NAMESPACE = "#" + $(this).parents("table.percentSort").attr("id");

   // with the namespace established, we can use the jQuery selection
   // syntax to use this namespace as our prefix for all of our remaining
   // searches.  Notice how the ambiguity is removed for our search
   // by CLASS of "percent" and "percentTotal"
   // This solves our namespace issues

   var sum = $(NAMESPACE + " input.percent").sum();
   var totalField = $(NAMESPACE + " .percentTotal");

So, by simply adding one line of code, you can encapsulate the widget and prevent it from overlapping functions (often incorrectly) with other instances of the widget (or even other widgets that have the unfortunate consequence of using the same names for IDs or classes). This type of coding is very common in plug-in code. The well-written plug-ins will be written with namespace issues in mind, while you'll find the poorly written ones will ignore them. As you can see in this example, it's relatively straightforward to use it in your own code, and it can lead to a great amount of time saved down the road if your page becomes more complex. For this reason, I would recommend you start your jQuery code with namespace issues in mind right away, and use this solution for all of your own coding.

Remember: When starting your own jQuery code, always start with the namespace solution included. You can use the solution above, or create your own. This will allow your code to grow in complexity without this ever becoming an issue.

What's new in 1.3

In this last section of the article, I wanted to go through the new features that were introduced in the 1.3 release of jQuery. The 1.3 release was a big release in terms of performance, and for this reason alone, you should move up to this release level in your own code. However, there were several small additions to the core code that can help you improve your own code.

The first addition to the 1.3 core was the live()/die() functions I discussed in this article. These were probably the most important addition, as you can tell, because they earned an entire section of the article. The other main addition to the 1.3 core is the jQuery.Event class, which encapsulates the events that occur on the page in an Object. This is extremely beneficial for event-intense applications, because it offers a well contained object that relays all the information you'll need concerning the event. Information such as the type, the target, the X and Y coordinates, and even a timestamp. This information may have been available before 1.3, but it was not as well documented, and certainly not as well encapsulated. Finally, the last addition to the 1.3 release will be transparent to you the developer, but it is still noteworthy. In this release, there is no more browser sniffing, where there are specific if statements for a browser or version. You can imagine the spaghetti code when dealing with all of the browsers it must support. This also added a certain decay to each jQuery version, because browsers are always coming into and going out of use. Instead, they detect functionality in the browser, while being browser type/version agnostic. This means as new browsers are created, and old ones disappear, the jQuery version will never need to be updated. Good for sites that don't want to upgrade their jQuery versions every year and the testing that would require.

Conclusion

This brings to a close this article offering you some tips on how to turn your good jQuery code into great jQuery code. jQuery is so simple to use (and such an improvement of stand-alone JavaScript), that it's quite easy to write good jQuery code. Most developers can be up and running in a few minutes. However, there is a distinction between writing good code and great code. Great jQuery code is concerned with performance as the page complexity increases. Great jQuery code thinks ahead to where a page is going, not where it is now, and codes for that expectation. Great jQuery code is designed for the most complex application possible, and then easily handles the simple stuff you throw at it.

This article introduced you to 5 concepts that can help your good jQuery code become great jQuery code. The first concept was the use of the bind()/unbind() methods. These methods are quite useful in attaching/detaching events to your page elements, when you don't want them attached to your code for the page's life. This can be important in performance situations, where events can ripple through the page, or it can be used in certain user interface situations. The second concept was the usage of the new live()/die() features in 1.3. These functions allow your events to be dynamic, just like your page elements. As page elements get added in your Web appliction, these functions allow your code to grow with your page, which wasn't possible before this release. You want your event handling to be as dynamic as your page. The third addition was the Ajax Queue/Sync plug-in, which should be used to regulate and control your Ajax calls to the server in situations where they could grow out of control (from a client perspective), and when the order in which they are returned is important. The fourth recommendation was to write as much of the page setup code in your jQuery code as possible. This allows your HTML to be simpler to write, and gives you more power when setting up your page. Finally, the last step was to utilize a namespace solution (either mine or your own) in your code to prevent widgets from overlapping functions and causing errors on your page. Each page element and widget should be self-contained and not interfere with other aspects of the page, and this solution will prevent that.

These 5 tidbits are by no means difficult. In fact, for 4 of them, it's as simple as changing one line of code. However, it's important to understand how these solutions can be used in your code. Like everything else, if they're used improperly they wouldn't help your code, and could actually hurt it. My recommendation is to start using these 5 things as soon as you start coding your jQuery on a page. Like every developer can tell you, feature creep is a standard part of our lives. You do not want to have to redesign your entire Web application because you made a poor decision at the beginning, or if you're being forced to change something because your manager demands it. Start your code with a great application in mind, and use these suggestions to get you there.

Finally, this brings to a close this second cycle in my jQuery series. These last 5 articles have brought you up to the next level in your jQuery profiency, and by now you should be able to create any type of application using the library. My final recommendation is to just play around with the code for awhile and experiment. You'll not only learn a lot, you may create the next cool thing on the Web!


Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=412500
ArticleTitle=Advanced jQuery
publish-date=07142009