Tuesday, May 18, 2010

Unobtrusive JavaScript and jQuery

What is Unobtrusive JavaScript?
jQuery was originally designed to follow the concept of Unobtrusive JavaScript. This philosophy of web page design considers any JavaScript expressions or statements embedded in the <body> of HTML pages, either as attributes of HTML elements or in script blocks placed within the body of the page, to be incorrect.

After reading this, I’m sure you’re nodding your head and thinking, “YES! that’s excellent...but wait, how do we link any JavaScript behavior to the elements that initiate that behavior?” The simple answer to this is somewhat a purest response - put all of your javascript in the head section of your page and then register all of your element behavior when the page loads. A simple before and after example is shown below:

Why would you want to do this in the first place? There are several reasons why this is a good approach to take. Firstly, it is much cleaner - you don’t have script elements scattered throughout your page. Secondly, it allows you to gracefully handle browsers that have disabled JavaScript. Lastly, it is much more stable - all behavior is registered after a given element is determined to exist within the page.

The cost of such an approach? It requires more code be written for one. But most importantly, it requires that each developer be disciplined enough to follow this approach as well as employ good coding practices to client-side script.

Introducing jQuery

The basis of jQuery selection is founded on a concept originally defined with the introduction of CSS known as selectors. A selector provides a way of concisely locating an element within a page’s DOM.
For example, the CSS selector

td a

refers to the group of all links (anchor tags - <a> elements) that are nested inside a table cell <td> element.

jQuery makes use of this same concept and then some. It supports the selector logic that is commonly supported by all major current browsers as well as more powerful ones not yet fully supported by most browsers - from the CSS 3 specification. The jQuery site provides a nice list of selectors that can be used at the following address - http://api.jquery.com/category/selectors/. An example selection by jQuery is done like so

jQuery(selector)

or

$(selector)

So in order to select the same anchor tag group as explained earlier (using the $ abbreviated alias reference used in our second example) our code would like like

$(“td a”)

The $() function returns an object that contains an array of the DOM elements that match our criteria. This jQuery wrapper object provides many useful predefined methods that can act upon this array of elements.

One very useful feature of many of the methods provided by the wrapper object is that they support method chaining. If you’re not familiar with method chaining, it is when one method’s return value can then be manipulated by the next method in the chain - much like how an assembly line works. To make this concept a little more concrete look at the following

$(“div”).methodA().methodB().methodC();

How this is interpreted is that the $(“div”) selects all <div> elements within the DOM and wraps them in our jQuery object. This object is then sent to the methodA() method which modifies the list of DOM elements. These modified elements are then sent to the methodB() method where they are modified further and so forth. This is a slight simplification, of course, but it captures the underlying concept. Because these methods work on the entire array of selected elements, there is never a need to iterate over each of the elements, this is done implicitly for us!

Adding Behavior to the Page Once the Document Structure has been Loaded and Parsed

To avoid having to wait for all resources to load into the browser as what happens with the document.onLoad() function, we utilize a jQuery utility that executes a function as soon as the document structure has been loaded and parsed by the browser, but prior to any other resources (images in particular) have been fully loaded. This helps to prevent pages that load very slowly from having a jarring effect on the user by changing behavior once all resources have completed downloading. This is accomplished via the jQuery document ready handler -
$(document).ready(function() {
  // function logic to add behavior, etc.
});
The short hand notation for this - $(function() { /* function logic */ }); performs the exact same but is more concise in its syntax. An added benefit of using the document ready handler is that you can call it multiple times within the page and each handler will be executed in the order it appears in the rendered page as soon as the DOM is ready to be manipulated (this is in vast contrast to the document.onLoad() function which allows only one call per page).

Extending jQuery to Provide Custom Behavior

jQuery was designed specifically to include only the most utilized functions as its core library but allow any developer to easily extend its behavior.
$.fn.myfunc = function() {
    return this.each(function() {
        // perform custom behavior logic
    });
}
This example shows how simple it is to extend jQuery to include new behavior while still working perfectly in concert with other jQuery functions. To briefly explain what is going on, the $.fn.myfunc defines a new extension wrapper called myfunc. Inside the new function we call the jQuery function each which iterates over the current DOM element and returns the results as a wrapped collection and so will fit seamlessly into any other chain of jQuery function calls. The reference to this within the function is a pointer to the specific DOM element for the current iteration.

This function would be used by first selecting your DOM context using the jQuery selector syntax just discussed and then chaining our function to this selection (and chaining further method calls after our function if need be) such as the following:

$(“form#ccform input”).myfunc();

As you can imagine, this is a very powerful extension architecture and requires only a small amount of scaffolding code in order to create new behavior.

Creating DOM Elements

Although, I’m not entirely fond of this idea, the $() function allows you to dynamically create and add arbitrary HTML elements by passing a valid HTML string as its parameter.

No comments:

Post a Comment