jQuery: Write less, do more

By Hans S. Tømmerholt

For information on other JavaScript toolkits, check out our Introduction to JavaScript toolkits article

Introduction

jQuery is a JavaScript toolkit designed to simplify various tasks such as element selection, DOM manipulation, event handling, AJAX and animations. Its tagline is “Write less, do more”. It’s aimed at a broad audience, as they say “from hobbyists to businesses”. It has support for Opera out of the box, so you can pick it up and use it right away.

Download jQuery now to get started. Check out the documentation, the API and some of the tutorials related to the toolkit.

Some vital statistics:

  • It has a lightweight footprint - about 20KB in size in compressed form.
  • It supports CSS3 selectors and basic XPath syntax.
  • It has cross-browser support, claiming to work in IE 6.0+, FF 1.5+, Safari 2.0+ and Opera 9.0+

In the following article, I’ll show you some of the things you can do with the toolkit.

The article is based on jQuery 1.1.4. Since this article was written, jQuery 1.2.1 has been released. In this release the XPath selector syntax was separated out into it’s own plugin. The jQuery UI project is also picking up steam, offering UI widgets like Sortables and Draggables.

Table of contents

  1. The magical $, or: Find things, do stuff
  2. The power of chaining
  3. Getting manipulative: The ‘Do stuff’ part
  4. A little bit of bondage, i.e. event binding
    1. Gotcha: $.ready, DOMContentLoaded and external style sheets
  5. jQuerying your AjAX
  6. Ext-animate! Ext-animate!
  7. Examples
    1. Collapsable list
    2. High score table
  8. Summing it up
  9. Resources

The magical $, or: Find things, do stuff

The basic philosophy in jQuery is to find things, ie select a set of DOM elements, and do something to them. To this end, jQuery has a powerful selection mechanism making use of CSS and XPath syntax. The starting point of the framework is the magical $, which is an alias for the jQuery class. JavaScript doesn't reserve the $ for anything so it can be used as a variable or type name. Everything is accomplished by constructing a jQuery object and giving it some arguments. The simplest example is to retrieve an element with a given id:

$('#container');

In this case an object of the jQuery class is created, the $(...) works like a constuctor. The object looks up the element identified by the given id and stores it for further processing.

The equivalent DOM statement would be:

document.getElementById('container');

The argument to the $ constructor can be an XPath or a CSS selector expression. XPath is a language for selecting parts of an XML document, navigating up and down the DOM tree. Selections can be constrained by various expressions, selecting elements which have certain attribute values, which is the nth-child of another element and so on. CSS selectors are used to define style rules in style sheets, eg saying that all instances of this class of tables should have a green font color. The syntax is simpler and less powerful than XPath, but useful enough for most purposes.

The combination of these two syntaxes allows you to do various selections ranging from the simple to the complex in jQuery:

$('a'); //All anchors in the document (CSS/XPath)
$('div.container'); //All divs with a container class (CSS)
$('div[@class=codeSnippet]/code'); 
//All code elements that are direct children of divs in the document with the class 'codeSnippet'. (XPath)

The $ object actually produces matches, which means it can contain several elements which are the result of the query. In the first example only one element could match the expression, as it used an id, but in the cases above you get multiple elements.

When supplying only one argument, the entire document is searched for matches. You may supply an optional extra argument context, which limits the scope of the search:

var container = document.getElementById('container');
$('div[@class=codeSnippet]/code', container );

In the example we select a specific container element, and then search among it’s descendants for matches. The context can be either a DOM element or another jQuery object, which means you can do this:

$('div[@class=codeSnippet]/code', $('#container') );

For comparison, Opera has experimental built in support for XPath with DOM bindings, as described in the DOM Level 3 XPath specification. The following code would accomplish the same.

var result = document.evaluate('div[@class=codeSnippet]/code', document, null, 0, null );
var ele = result.iterateNext(); //and so on

The $ object itself wraps an array of DOM elements matched by the given query. This means you can examine the length of the search by using a length property or size function:

$('a').length;
$('a').size();

It is possible to retrieve the actual elements found by the query by using the get method. By default it returns all the matched elements as an array. If you supply a number argument, it returns the elements at that position in the array.

var anchors = $('a').get();
var first = $('a').get(0);

Note: The 1.1.4 version deprecates the XPath syntax, and 1.2.1 version supplies this in it’s own plugin.

The power of chaining

Almost all of the methods on the jQuery object return another jQuery object. This leads us to the second strength of the toolkit: Method calls can be chained indefinitely. Let’s look at an example:

$('a').slice(0,3).each( function (i) {
    alert(this);
});
$('a') selects all anchors in the document. slice(0,3) selects the first three elements from the previous result. each() loops through and calls the given function for each of them.

It’s possible to do much more complex chaining. the end() function undoes the last change to the selection.

$('a')
  .filter('[@href^=http://]').append('<img src="external_arrow.png" />')
  .end()
  .not('[@href^=http://]').append('<img src="internal_arrow.png" />');
In this example we first select all anchors elements in the document, then filter that selection down to those anchors with a href attribute starting with http:// (.filter('[@href^=http://]')). An image of an arrow pointing away from the text is added to each matched element. Then call to end() undoes the filtering of the selection, restoring it to all anchors in the document again. The next snippet selects those anchors having an href attribute that doesn't start with http:// (.not('[@href^=http://]')), and adds an image of an arrow pointing towards the text.

Getting manipulative: The ‘Do stuff’ part

jQuery supplies several methods for manipulating the matched elements. Examples include affecting CSS, and adding content before, inside and after elements. The simples is CSS manipulation, demonstrated in the following example:

$('a').css( 'text-decoration', 'none' );

In this case we select all the anchors in the document and remove their underline. As other methods, the css method applies the style to all matched elements. A different shorthand takes a single dictionary of properies and sets all the properties for all matched elements:

$('a').css( { text-decoration: 'none', color: 'green'} );

The equivalent DOM statements would be:

var anchors = document.getElementsByTagName('a');
for ( var i = 0, a; a = anchors[i]; i++ )
{
    a.style.textDecoration = 'none';
    a.style.color = 'green';
}

If you give the css method a single string property, it will retrieve the value for that property on the first element in the list of matched elements.

In addition to manipulating CSS itself, jQuery also provides you with a set of methods to manipulate the DOM. The functions prepend() and append() allow you to add content into an existing node at the beginning and the end respecticely.

$('#status').append( '<span class="warning">red</span>' );

The argument will be parsed and placed into the node. The similar methods before() and after() add content before or after the matched nodes:

$('#highscore li:nth-child(3)').before('<li>696 Arve</li>');

Here, a new li element is added to a high score list before the current third li element. The appendTo and prependTo methods do the opposite: They take the string to be evaluated and appends it to the jQuery or DOM argument given to the function. The wrap() method is interesting because it wraps each matched element in a supplied HTML or element structure.

$('#foo').wrap( '<div class="adhocContainer"></div>' );

Finally there are a few utility methods: empty() will clean out all content from the matched elements while clone() creates a deep clone of the matched elements for insertion elsewhere.

A little bit of bondage, i.e. event binding

Another favorite among toolkits is event binding. jQuery builds comprehensive support on top of the basic event model.

The first and most important for event binding is the $.ready() function. This is bound to the event DOMContentLoaded, which fires when the DOM is fully constructed, but before images are loaded. As such, this is not comparable to window.onload or body.onload, which fires when images are also loaded.

$.ready( function () {
    //Do init stuff here
});

jQuery supplies a handy shortcut for this. The above be be reduced to simply:

$( function () {
    //Do init stuff here
});

From there, jQuery has one general and several specific functions for adding event handlers. The general one is called $.bind(), and works the same way as the DOM addEventListener function:

$('#addCustomerButton').bind( 'click', function() {
  //Submit request
});

In this case we retrieve the element with the id addCustomerButton and add a click event handler to it. The second argument is the event handler function itself.

For each type of event there is a similar jQuery function. For example, for the click event there is a click() function. The above can thus be rewritten as:

$('#addCustomerButton').click( function() {
    //Submit request
});

Of other examples I can mention change(), keypress(), select(), scroll() and focus().

If you do not specify an argument to these functions, the corresponding event will be fired on the matched elements. I.e.:

$('#addCustomerButton').click();

This will fire a click event on the element with the id “addCustomerButton”

Gotcha: $.ready, DOMContentLoaded and external style sheets

In his blog, Nicolas Mendoza noted one cross browser problem with jQuery: The DOMContentLoaded event fires before images are loaded, but there is a disagreement as to whether or not it should wait until external CSS styles are loaded as well.

Currently Opera will fire the event without waiting for external CSS, while Firefox will wait until they are loaded. This causes a cross browser gotcha if you modify the CSS of your elements inside the $.ready() call. In Opera, your changes will potentially be overriden when the style sheet is finally loaded.

A workaround for this problem is to use the load event ($(element).load()) for situations where you change your CSS during the initialization phase.

jQuerying your AjAX

Any self-respecting JavaScript toolkit comes complete with functions which simplify AJAX and jQuery is no exception. jQuery supports both simple and advanced variants. use the $.get() and $.post() methods to send simple GET and POST requests respectively. Each take three arguments, the request URL, a set of parameters (optional) and a callback function (optional) for the resulting data. For example:

$.get( 'bookDetails.php', { 'isbn' : 'ISBN 0-201-63361-2' }, showDetails );
$.post( 'addCustomer.php' { 'name' : 'Darth Vader', 'e-mail' : 'darthvader@galacticempire.org' }, showReport );

In the first example, we retrieve information via a GET request sent to the PHP script. The CGI parameter isbn is passed. The showDetails function is added as an event handler for when a response is received. In the second example, data is sent to the server as a POST request, passing data about a new customer to add. The showReport function handles any responses.

But jQuery also allow you to control your queries with a more flexible variant. The $.ajax() function takes a single dictionary, which can set several properties of the request. Examples include the expected contentType and dataType (JSON, html, XML and script) and functions to call at various stages of the request.

$.ajax( { url: 'addCustomer.php',  type : 'POST', success : showReport, 
    data : {'name' : 'Darth Vader', 'e-mail' : 'darthvader@galacticempire.org} }, 
    dataType : 'json', error : showError } );

Another nifty aspect of jQuery’s ajax handling is the ability to set global defaults for several of the parameters. This is accomplished using the $.ajaxSetup() function. Like the $.ajax() function, it takes a single dictionary of properties:

$.ajaxSetup( { url : 'customerApp.php', type : 'POST', contentType : 'json', error : showError } );
$.ajax( { data : { type : 'add', name : 'Darth Vader' } } );
$.ajax( { data : { type : 'add', name : 'Darth Maul' } } );

As see in the example, once the properties are set, they are treated as default values for future calls to $.ajax().

A smart little function is load() which loads the results of an AJAX request into the matched elements. This is a simple way of adding in chunks of data from other sources much like the include function of for example PHP:

$('#overview-container').load('overview-toc.html');

Ext-animate! Ext-animate!

jQuery has support for animations and visual effects. The JavaScript toolkit has several methods for
specific visual effects such as fading, sliding, hiding and toggling, . For example:

$('.inactive').slideUp('fast');

In this case, any element with the inactive class slides up with a fast speed.

The methods hide(), show() and toggle() allows you to quickly hide and show elements on the page.

$('#commentControls').toggle();

In addition to these, there is a specialized method animate() which allows you to do more specific or complex animations. animate() takes a dictionary of CSS properties and values to animate to, ie if you write:

$('#foo').animate( { width : 100, height: 100 }  );

Then the element will be animated – it will go form its current width and height to 100 pixels wide and tall. There are three preset string values for the properties: ‘hide’, ‘show’ and ‘toggle’, which gives you often used effects. You can also supply an argument for the speed of the animation, using preset values of ‘slow’, ‘normal’ and fast, and a function to call once the animation has completed. The latter can allow you to chain animations, for example.

Note: jQuery 1.2 introduces animations of colors, which wasn’t present in the previous versions. Furthermore, the animation mechanism itself has become more powerful with for example chaining.

Examples

In this section I show some examples of how you can use jQuery. You can download the code examples for this article from here.

Collapsable list

The following shows a quick way of making a list where sublists can be collapsed and expanded by clicking:

<!DOCTYPE html>
<html>
  <head>
    <title>Collapsable list</title>
    <script type="text/javascript" src="jquery-1.1.4.pack.js"></script>
    <script type="text/javascript">
$( function () {
    $('li:has(ul)').click( function (evt) {
        if ( event.target != this ) {
            return;
        }
        $('ul:eq(0)', this).toggle();
        evt.stopPropagation();
    })
});
    </script>
  </head>
  <body>
    <ul>
      <li>A</li>
      <li>
        B
        <ul>
          <li>C</li>
          <li>
            D
            <ul>
              <li>E</li>
              <li>F</li>
              <li>G</li>
            </ul>
          </li>
          <li>H</li>
        </ul>
      </li>
      <li>I</li>
    </ul>
  </body>
</html>

Let's dissect the JavaScript code a little bit, and look at what is going on:

$( function () {
    $('li:has(ul)').click( function (evt) {
        if ( event.target != this ) {
            return;
        }
        $('ul:eq(0)', this).toggle();
        evt.stopPropagation();
    })
});

First of all, the $( func ) constructor is used as a shorthand for $.ready(). The fragment $(‘li:has(ul)’) selects all li elements which have a ul child. A click handler is added to all of these elements in one go: Calling a function on a jQuery object will usually apply it to all the matched elements in the query. The first if structure simply stops clicks from child elements of the li, so that only clicks on the text content of the li itself causes a change. Finally, $(‘ul:eq(0)’, this) selects the first ul child of the li (this) and toggles it using a jQuery utility function. Finally the event is stopped to avoid more handlers firing higher in the tree.

High score table

In this example I go through using a simple high score list, where people are pushed down and out of the list as higher scores are added.

<!DOCTYPE html>
<html>
  <head>
    <title>High score table</title>
    <script type="text/javascript" src="jquery-1.1.4.pack.js"></script>
    <script type="text/javascript">
$(function () {
    $('#scoreForm').submit( function (e) {
        var score = this.score.value;
        var name = this.name.value;
        var hs = $('#highscore');
        $( 'li', hs ).each( function () {
            if ( parseInt(this.firstChild.nodeValue.split(' ')[0]) < score )
            {
                $(this).before( '<li>' + score + ' ' + name + '</li>');
                $('li:last-child', hs).remove();
                return false;
            }
        });
        return false;
    });
});
    </script>
  </head>
  <body>
    <ol id="highscore">
      <li>276 Rune</li>
      <li>135 Mathieu</li>
      <li>97 Christian</li>
    </ol>
    <form id="scoreForm" method="POST" action="">
    <p>
      <label>Score: <input type="text" name="score"></label>
      <label>Name: <input type="text" name="name"></label>
      <input type="submit" value="Save score">
    </p>
  </body>
</html>

Let's take a closer look at the code:

$(function () {
    $('#scoreForm').submit( function (e) {
        var score = this.score.value;
        var name = this.name.value;
        var hs = $('#highscore');
        $( 'li', hs ).each( function () {
            if ( parseInt(this.firstChild.nodeValue.split(' ')[0]) < score )
            {
                $(this).before( '<li>' + score + ' ' + name + '</li>');
                $('li:last-child', hs).remove();
               return false;
            }
        });
        return false;
    });
});

When the DOM is loaded, we add a submit event handler on the high score form. When you submit a new score, the code looks through the current list and checks if the submited code is higher than one in the list. We loop through all li children of the high score list by doing $(‘li’ hs ).each(). The each() method takes a function which is called for each iteration. The second argument limits the span of the selection to the high score list itself. Note that this refers to the DOM element being processed. We add a new list item before the one we’re checking through $(this).before(). Finally, we remove the last list item in the list which is now below the threshold: $(‘li:last-child’, hs).remove().

Summing it up

jQuery has a very powerful selection mechanism, and this is at the heart of the toolkit. You can retrieve a set of elements and apply functions or otherwise change them. The syntax is easy to understand, and supports CSS selectors and XPath expressions. The chaining mechanism is also excellent, allowing you to do complex mutations with very little code. The code of the toolkit itself is small in terms of total number of functions and file size: 22kb compressed and 61kb uncompressed.

jQuery also provides several now standard aspects such as event binding, Ajax and animation. “All” toolkits have these, so what is special? The advantage to the event binding and animation implementation here, stems from the ability to apply them to a series of elements at the same time. The Ajax implementation is run-of-the-mill, with the exception of loading the results directly into a specific container.

Additionally, jQuery provides several utility methods which are nice to have. Among the less useful is a shorthand for checking which browser the reader is using. This is effectively user agent sniffing and should be avoided. Such sniffing means you’ll need to update your scripts every time a new browser version comes out. Use capability detection instead, checking if the browser supports a given feature, like XPath or similar. All in all, many of the features make your work a bit easier, but are not revolutionary by themselves. They all work cross browser, however, which is a big advantage.

The toolkit separates everything into one place, the jQuery class. This effectively means it plays nice with other toolkits and libraries which is a very good thing. The use of $ conflicts with a similar function in the Prototype library, but jQuery provides a way out by calling $.noConflict(), releasing the $ back to Prototype. You can alias the jQuery class by simply creating another variable refering to it.

jQuery is the basis for several user-contributed pluings which extend and transform its functionality. The website gives a comprehensive list of registered plugins, some of which are adopted as official plugins for the project. One example is jQuery UI project, which we mentioned earlier

If you apply this library and the jQuery object everywhere, your JavaScript code really starts looking different. This is also one of the goals of the toolkit: To transform the way you write JavaScript code. Ultimately, however, it’s still just an object which can be wrapped around any DOM element at any time, which means you can apply it only where you need it.

jQuery has solid API documentation and a host of tutorials to get you started. There is an active community around the toolkit, for example on the general mailing list where people are helpful and friendly. The list sees several hundred mails each day.

Resources

This article is licensed under a Creative Commons Attribution, Non Commercial - Share Alike 2.5 license.

Comments

The forum archive of this article is still available on My Opera.

No new comments accepted.