If you use jQuery in your web applications, chances are you've made it an indispensable part of your technology tool belt. jQuery is something that I personally can't live without when developing browser-centric applications that perform a lot of client-side manipulation. jQuery plays a key role in finding elements, handling events, manipulating the Document Object Model (DOM), making Ajax calls, and performing animations. On top of all that, the rich set of jQuery plug-ins available significantly increases productivity. If you're new to jQuery, you can learn more about it at the jQuery website and "Tour the jQuery Framework."

If you use jQuery frequently, you've probably come across helpful tips and tricks that you use in coding. In this article series, I hope to add a few tips to your jQuery tool set, as I describe some of the tips that I've found useful in jQuery-enabled applications. If you're new to jQuery, you will likely find the tips handy and be able to apply them right away. If you've been using jQuery for a while, you'll probably recognize several of the tips but will find one or two tips that are new and interesting.

Defining a Context with Selectors

Selectors are an essential part of jQuery that provide a great way to locate nodes in the DOM quickly and easily. For example, the following selector can be used to find all div elements with a class of panel on them:

                              var panelDivs = $('div.panel');

Selectors are well documented at the jQuery Selectors web page, so I won't rehash what they are or how to use them. However, I want to mention one simple yet powerful tip that many people new to jQuery will find useful. It's a tip that I've used over and over as I build applications.

Selectors can be used in the following way to find all elements with a class of panel on them:

                              var panels = $('.panel');

However, what if you want to grab all elements with a panel class that are children of a given parent container element? To better understand the scenario, consider the HTML shown in Figure 1.

<div id="emailContainer">
    <div class="panel">...</div>
    <div class="panel">...</div>
</div>
<div id="ordersContainer">
    <div class="panel">...</div>
    <div class="panel">...</div>
</div>

Assume that you already have a reference to the emailContainer div stored in a variable named emailDiv:

                              var emailDiv = $('#emailContainer');

How can you find all elements with a panel class on them within the emailDiv element? One technique is to use the jQuery find() function:

                              var panels = emailDiv.find('.panel');

Alternatively, you can supply a context object to the selector, as follows:

                              var panels = $('.panel', emailDiv);

Which way is better? The answer depends on whom you talk to, although some live performance tests imply that find() has a slight performance advantage. Nevertheless, knowing how to supply a context when using selectors is a useful technique in several scenarios.

Manipulating the DOM in a Loop

One of jQuery's greatest strengths is its ability to manipulate the DOM using a minimal amount of code. It's so easy to change things that we often don't think about what's happening under the covers. For example, consider the code in Figure 2, which appends nodes to an object named parentDiv.

var parentDiv = $('#emailList');

for (var i = 0; i < 100; i++) {
  parentDiv.append('<div>' + i + '</div>');
}

This is the standard way to append nodes into a given parent. Although it works, it isn't optimal because jQuery has to constantly perform DOM operations in the loop. You might not notice a problem when looping through 100 items, but as that number increases, performance can start to degrade. From a performance standpoint, we can take a more efficient approach in appending child nodes, as shown in Figure 3.

var parentDiv = $('#emailList');

var divs = '';
for (var i = 0; i < 100; i++) {
  divs += '<div>' + i + '</div>';
}
parentDiv.html(divs);

The code in Figure 3 touches the DOM only once after the loop has finished, resulting in better overall performance. Keep in mind that you can always manipulate the DOM directly as well and improve performance by using techniques such as document.createDocumentFragment() and others.

Although I prefer to use jQuery functions whenever possible, sometimes it's helpful to step outside of jQuery to perform a particular task and use the native DOM API instead. A test-case page shows different techniques that can be used for updating the DOM while using a loop and performance results for the techniques.

Using the on() Function

jQuery provides several functions that can be used to handle events. Choices include shortcut functions such as click() and hover(), the live() function, the delegate() function, and the bind() function. The live() and delegate() functions can be used to handle event-bubbling scenarios such as rows in a table being clicked, whereas the shortcut functions act as wrappers around the bind() function.

jQuery 1.7 and later include a new function named on() that wraps the previous event-handler functions into one convenient API that can be used to handle everything from simple events to more complex event-bubbling scenarios. Here's an example of using on() to handle a simple click event:

                              $('#myButton').on('click', function() {                                  alert('Clicked the Button');                              });

You can always use the shortcut click() function instead. I generally prefer the shortcut functions when handling single events because they let me eliminate quotes from the equation.

When handling multiple events, on() is quite useful. You can use its mapping functionality, shown in Figure 4, to map events to callback functions.

$('#MyTable tr').on({
  mouseenter: function(){
    $(this).toggleClass('over');
    //perform other functionality needed during mouseenter
  },
  mouseleave: function(){
    $(this).toggleClass('over');
    //perform other functionality needed during mouseleave
  }
});

The on() function can also be used in place of live() or delegate() to detect when a child of a given parent container has been clicked. For example, when a table has a large number of rows that need event handlers to detect when a user clicks or hovers over them, the on() function can be used. Rather than attaching an event handler to 1,000 rows, on() can be used to attach a single event handler (or map event handlers) that handles row events to the containing table's tbody element. Here's an example of using on() for this purpose:

                              $('#customerTable tbody').on('click', 'tr', function() {                                  alert('Clicked a Row');                              });                              

This code will cause click events triggered within a table to bubble up to the containing tbody element that handles them. It's very efficient to handle events this way because only one event handler is added, yet thousands of potential row events can be handled. Future rows that are dynamically added to the table are also handled automatically. If you're wondering what happened to jQuery's delegate() function (which does the same type of thing), it can still be used. However, on() is now the preferred event function to use with jQuery 1.7 and later.

Use a CDN When Possible

Although many developers load jQuery from a local script resource stored on their server, in some cases another option is preferable. Microsoft and Google both expose the jQuery script through their content delivery network (CDN). There are several advantages to using a CDN, such as caching of the jQuery script and loading it from regional servers. For example, if a user visits a site that uses the Google CDN to load jQuery and that same user then visits your site that also loads jQuery from the CDN, the script will be pulled from the browser cache instead of being downloaded again. In addition to caching benefits, typically the script will be retrieved from a CDN server in the user's region, which speeds up the overall process.

Although there are several benefits to loading jQuery from a CDN, what if the CDN is down, the network between the user and the CDN is unavailable, or another factor comes into play? Without the script, your application won't be able to do much, of course. Fortunately, you can easily add a local jQuery script as a fallback using the syntax shown in Figure 5.

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>

<script>window.jQuery || document.write('<script src="js/libs/jquery-1.7.1.min.js"><\/script>')</script>

This script first attempts to load jQuery from the Google CDN. However, instead of assuming that the file was successfully retrieved, the code checks for the existence of the jQuery object immediately after trying to load the script. If the jQuery object isn't available, a local script is loaded using the document.write() statement. This is a good practice to follow because it lets you take advantage of CDN features while also having a local fallback if something goes down. You'll see this type of code used in templates such as HTML5 Boilerplate.

Storing Data Using the data() Function

Over the years that I've built web applications, I've often run into scenarios where I need to store data about a particular div or row so that I can easily retrieve it without going back to the server. For example, you might have a table full of customer rows and want to store address, city, customer ID, and other data that might not be shown in the table. As the user clicks a row, you can then pull the data stored with the row and show it in a dialog.

Before jQuery existed, developers would use custom attributes added to an element or use JavaScript arrays to handle this task. If a single value needed to be stored, the id attribute would often be used, assuming the value was unique. In situations where multiple values needed to be stored, the following code could be used to associate values with a given row rather than relying on arrays that hold the data in JavaScript:

                              row.setAttribute('city','Scottsdale');                              row.setAttribute('state','Arizona');                              

As the user clicks a row, calls to getAttribute('city') and getAttribute('state') can be used to retrieve the city and state values. Although this technique still works today, it's generally frowned upon. Even though you can always store data in JavaScript objects, jQuery has a data() function that can be used to easily associate data with a given DOM object and even work with HTML5 data attributes. Here's what the official jQuery documentation says about the data() function:

"The .data() method allows us to attach data of any type to DOM elements in a way that is safe from circular references and therefore from memory leaks."

Using the data() function is simple. For example, to associate a city value with a row (assume row is a <tr> element), you can use the following code:

                              $(row).data('city','Scottsdale');

To retrieve the city value from a row, you can write the following code:

                              var city = $(row).data('city');

The data() function can also be used to retrieve HTML5 data attributes from elements. Assume the following element exists in a page:

                              <div id="custContainer" data-city="Scottsdale" data-state="Arizona">                                  ...                              </div>

To retrieve the value of the data attributes, you could use the following jQuery code:

                              var custDiv = $('#custContainer');                              var city = custDiv.data('city');                              var state = custDiv.data('state');

In addition to providing this functionality, the data() function handles storing object literals. Figure 6 shows an example of creating and storing an object literal that holds customer data.

var cust = {
    firstName: 'John',
    lastName: 'Doe',
    city: 'Scottsdale',
    state: 'Arizona'
};

$(row).data('customer',cust);

The Account at a Glance sample application contains a more robust example of using data(). (You can download the app here.) Figure 7 shows a function that demonstrates how data() can be used to store HTML rendered using client-side templates.

var TileBinder = function () {
    var templateBase = '/Templates/',

    bind = function (tileDiv, data, renderer) {
        var tileName = tileDiv.attr('id');
        $.get(templateBase + tileName + '.html', function (templates) {
            $('body').append(templates);
            var acctTemplates = [
                tmpl(tileName, 'Small', data),
                tmpl(tileName, 'Medium', data),
                tmpl(tileName, 'Large', data)
            ];
            tileDiv.data().templates = acctTemplates;
            tileDiv.data().data = data;
            renderer(tileDiv);
        });
    },

    tmpl = function (tileName, size, data) {
        var template = $('#' + tileName + 'Template_' + size);
        if (data != null)
            return template.tmpl(data);
        else
            return template.html();
    };

    return {
        bind: bind
    };
} ();

This example doesn't use a string value to set the data key but instead uses two expando properties named templates and data, respectively (both are custom properties).

Here's an example of accessing the template data:

                              var template = tileDiv.data().templates[0]; //grab the first template's HTML

A Productivity Boost

In this first article on jQuery tips, I've covered some of the key techniques I wish I'd known when I first started programming with jQuery as well as some newer techniques. In "4 More Web Development Tips to Improve Your jQuery Coding," I'll cover additional tips and show how they can enhance your web applications and make you more productive as a web developer.