Writing Effective jQuery Code: Use JavaScript Patterns

Although the subject of software design patterns is far too broad a topic to cover here in depth, understanding and implementing basic patterns can go a long way toward making your code more maintainable.

For more on software design patterns in general, the classic “Gang of Four” book, Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, is the indispensable reference. Check it out if anything in the following section strikes a chord with you.

In this section, you learn about techniques that limit global namespace pollution, provide logical module initialization, and allow for easier collaboration among multiple developers.

1. Creating an Application Namespace with a Singleton

One of the simplest techniques you can use to structure your code is to use well-chosen namespaces. This pattern is commonly called a singleton.

Before you look at the singleton, look at the unstructured approach it approves upon. This style of code is still common and was especially prevalent in the earliest days of JavaScript programming. For a long time, it was typical to simply present a series of function declarations defined at the global space. This throwback approach is shown in Listing 10-6.

LISTING 10-6: The old-school approach

function myAppInit(){

//code to initialize your application

}

function myAppDashboard(){

//your dashboard feature goes here

}

function myControlPanel() {

//control panel code goes here

}

function myAppSettings(){

//code that updates settings goes here

}

//kicked off with the ready() function

$( document ).ready(myAppInit)

Code snippet is from multiple-functions.txt

Though there’s nothing wrong about this pattern in a technical sense, it does present some compatibility hurdles.

To see the compatibility issues, you can just pop open Firebug and navigate to the DOM tab. There you’ll see all the functions created available in the global namespace. This is illustrated in Figure 10-1.

It doesn’t take long for a JavaScript developer to discover that populating the global namespace with multiple variables can cause serious issues. For an obvious example, imagine naming a variable i, _, or $ in the global namespace. That’s going to cause a problem somewhere down the line.

More obscure names might be safer, but they’re still not safe, so don’t think that naming your variables after characters from Jabberwocky is going to save you from variable collisions.

Just wait until you have two Lewis Carroll freaks on a project.

So, instead of merely dropping a series of functions into a global namespace, you can alternatively leverage a single variable to hold all of your application code. In the simplest example, this variable is an object literal, which in turn will contain your application modules and properties. A simple example is shown in Listing 10-7.

LISTING 10-7: A singleton

var myApp = {

init :   function(){

//code to initialize your application

},

dashboard:   function(){

//your dashboard feature goes here

},

controlPanel:  function() {

//control panel code goes here

},

appSettings:   function(){

//code that updates settings goes here

}

}

//kick it off with ready()

$( document ).ready( myApp.init )

Code snippet is from singleton.txt

Firing up Firebug with this new structure shows that there’s only the single myApp variable in the global namespace. You can see this in Figure 10-2.

Even in this simplified example, that cuts down the possibilities of a variable name collision significantly. If your application has dozens or even hundreds of similar methods and properties, the benefit of an application namespace is even greater.

There’s more to namespaces than preventing bugs. This simple singleton can be expanded to create separate objects for individual application features and/or site sections.

This allows for multiple developers to work in a single application namespace across multiple files, allowing for greater maintainability and less chance of conflicts in version control or otherwise stepping on each other’s toes. Though individuals might be able to get away with it, teams working with a monolithic JavaScript file can cause disasters.

You can see the simple object literal you’ve been working with broken out into multiple application objects in Listing 10-8. Structurally, each site section would be broken out into a separate file. These would be common.js, dashboard.js, controlPanel.js, and settings.js in this example. With the exception of common.js, individual developers could own each site section and file without really worrying about what anyone else was doing. common.js would be owned by the project lead, both for the sake of maintenance and because the question of what’s truly “common” has some nuance to it.

LISTING 10-8: A simple application framework

 var myApp = {

//a common object holds code common to all application sections

common : {

init :   function(){

//common code to initialize your application

}

}

};

myApp.dashboard = {

//your dashboard feature has an init function as well.

init : function(){

//init code

},

update : function(){

//code to update the dashboard

},

render :   function(){

//code to render the dashboard

}

};

myApp.controlPanel = {

//your control panel feature has an init function as well.

init : function(){

//init code

},

settings : function(){

//code for control panel settings

}

};

myApp.settings = {

//your settings page has an init as well

init : function(){

//init code

},

update : function(){

//code to update site settings

}

}

Code snippet is from a-simple-application-framework.txt

One thing to note is that, although working in multiple files is a best practice, actually serving multiple files to your users is definitively not suggested. If you’re working in multiple files in your project, it’s worth looking at ways to automatically concatenate (and minify) your files together into a single file. A good starting point for this kind of task is the HTML5 Boilerplate project and its associated build script. You can find it at http://html5boilerplate.com/. As an alternative to a static build script, a script loader like LABjs http://labjs.com/ offers a JavaScript-based method for loading files on an as-needed basis.

2. The Module Pattern

A variation on the singleton, the module pattern was created by Douglas Crockford, Chief JavaScript Architect at Yahoo! and author of the JSON data interchange format. It was popularized in a post by Eric Miraglia on the Yahoo! User Interface Blog in June 2007 (http://www.yuiblog .com/blog/2007/06/12/module-pattern/) and is cover in depth in Crockford’s book, JavaScript: The Good Parts (O’Reilly Media, 2008). It enhances the encapsulation provided by the singleton and adds the ability to create private methods and properties.

The module pattern consists of three main components: a namespace similar to the one leveraged in the previous example, a function that immediately executes, and the function’s return object, which contains publicly available methods and properties. You can see a simplified example in Listing 10-9.

LISTING 10-9: The JavaScript module pattern

 //Our app namespace. We pass in the jQuery object to shorten lookups

var myApp = function ( $ ) {

// private variables and methods, only available within this myApp

var message = “not directly accessible from outside the module”;

function multiplier ( x,y ) {

return x * y

};

//the return object contains the public

//properties and public methods

return {

init : function(){

//initialize the app

},

prop : “42”,

specialNumber : function () {

//access to our private method

var num = multiplier( 7 , 6 );

return “Our special number is definitely ” + num;

},

//we provide controlled access to private variables

shareMessage : function( arg ){

if ( arg === “open sesame” ) {

return message + “/unless you know the magic word”;

} else {

throw new Error( “You need to know the magic word” );

}

}

};

}( jQuery );

Code snippet is from module-pattern.txt

Examining this sample app on the console illustrates the way the module works. The private variables can be referenced within the module body, but can’t be called directly from the outside. You are able to grant access in special cases through methods exposed in the return object. As you can see in Listing 10-10, examining the module using console.log illustrates both cases.

LISTING 10-10: Examining the module pattern in the JavaScript console

>>> console.log( myApp.message ) undefined

>>> console.log( myApp.multiplier() )

TypeError: myApp.privateMethod is not a function

>>> console.log( myApp.shareMessage( “please?” ) )

“You need to know the magic word”

>>> console.log( myApp.shareMessage( “open sesame” ) )

not directly accessible from outside the module,unless you know the magic word

>>> console.log( myApp.prop );

42

>>> console.log( myApp.specialNumber() );

Our special number is definitely 42

Extending the pattern to additional modules is straightforward. Listing 10-11 illustrates a dashboard module, complete with a private configuration object and a public method to allow gated access to dashboard configuration based on a set of predefined criteria. If you’ve got experience with languages like C#, Java, and C++, then you’re familiar with the usage of the private and public keywords. This pattern allows JavaScript to provide similar functionality. It’s not every day that you’ll need to use this kind of protected access, but it’s important to know that it’s there when the need does arise.

LISTING 10-11: A dashboard module

myApp.dashboard = function ( $ ) {

// private variables and methods

var config = {

“color” : “blue”,

“title” : “my dashboard”,

“width” : “960px”

};

return {

init : function(){

//initialize the dashboard

},

//updateConfig allows for monitored configuration

//of the private config object

updateConfig : function( obj ) {

if ($.inArray(obj.color, [“red”, “blue”, “green”, “purple”] !== -1)) {

config.color = obj.color;

}

config.title = obj.title || config.title;

config.width = obj.width || config.width;

}, render : function() {

//renders the dashboard

var $dashboard = $( “<div/>” ).html( “<h1/>” )

$dashboard.text( config.title )

.css(

{ “width” : config.width,

“color” : config.color }

);

$( “#main” ).append( $dashboard );

}

};

}( jQuery );

Code snippet is from advanced-module.txt

3. The Garber-Irish Implementation

The following pattern expands on the singleton and module pattern with cleverly chosen markup and style hooks to create a seamless initialization pattern for sites and applications.

This pattern was introduced by in 2009 by Paul Irish in a blog post entitled “Markup-based unobtrusive comprehensive DOM-ready execution” (http://paulirish.com/2009/markup- based-unobtrusive-comprehensive-dom-ready-execution/) and later modified by Jason Garber in a blog post entitled “Extending Paul Irish’s comprehensive DOM-ready execution” (http://www.viget.com/inspire/extending-paul-irishs-comprehensive-dom-ready-execution/).

In short, it leverages a small utility script and either CSS classes or HTML5 data attributes on the body element to selectively fire init() events depending on the site section or page type. This pattern alleviates the problem of trying to hunt down multiple $(document).ready() calls strewn throughout an application, instead providing a consistent, structured approach that works across multiple domains (server code, JavaScript, and style).

You can use either version, depending on your taste, so I’ll share both flavors. First up is Paul’s original version.

3.1. The Original Recipe

The pattern starts with a typical object literal. This variation has a couple of specific enhancements that are shown in Listing 10-12. Take note of the sections with init methods and the common section, which contains a method called finalize. The init methods are designed to fire on $( document ).ready() when the relevant section or feature is needed. This need is determined by the HTML structure you’ll see shortly. The finalize methods are for code that needs to execute as soon as possible but doesn’t affect the initial load of the page.

LISTING 10-12: Object literal enhanced for “markup-based unobtrusive comprehensive DOM-ready execution.”

myApp = {

common : {

init : function(){

//init()

},

finalize : function(){

//we’re saving these for a little later

}

},

dashboard : {

init : function(){

//dashboard init

},

settings: function(){

//dashboard settings

},

render : function(){

//render

},

}

}

Code snippet is from garber-irish-object-literal.txt

The next component is a small utility script, which parses the body id and className and attempts to fire functions based on the patterns exposed. This code, taken directly from Paul’s original post, is shown in Listing 10-13.

LISTING 10-13: “Markup-based unobtrusive comprehensive DOM-ready execution.”

/* from Wrox.com

* http://paulirish.com/2009/markup-based-unobtrusive-comprehensive-dom-ready-execution/

* License: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication

*/

UTIL = {

fire : function(func,funcname, args){

var namespace = myApp; // indicate your obj literal namespace here

funcname = (funcname === undefined) ? ‘init’ : funcname;

if (func !== ‘’ && namespace[func]

&& typeof namespace[func][funcname] == ‘function’){

namespace[func][funcname](args);

}

}

loadEvents : function(){

var bodyld = document.body.id;

// hit up common first.

UTIL.fire(‘common’);

// do all the classes too.

$.each(document.body.className.split(/\s+/),function(i,classnm){

UTIL.fire(classnm);

UTIL.fire(classnm,bodyId);

}

});

UTIL.fire(‘common’,’finalize’);

}

};

// kick it all off here

$(document).ready(UTIL.loadEvents);

The final piece is an HTML document with a body tag with a class and an id that correspond to site sections or features mapped out in the object literal:

<body id=”settings” class=”dashboard”>

Putting them all together would fire events in the following order:

  • common.init()
  • dashboard.init()
  • dashboard.settings()
  • common.settings()

This pattern can expand beyond the single class shown in the previous example. Multiple classes can be strung together, which allows multiple features to fire automatically. This is especially cool because once it’s in place, it allows JavaScript features to be initialized without having to touch a line of JavaScript code. An engineer working in PHP or Java can simply append a class to a page and know that the proper initialization code for a module will fire.

3.2. Using HTML5 Data Attributes Instead of CSS Classes and IDs

Jason Garber’s variation is very similar, with the key difference that it uses HTML5 data attributes to trigger the execution.

If you’re unfamiliar with them, HTML5 data attributes are defined methods for storing custom data in HTML attributes. Application developers had long stored string data in custom attributes, so this is a case of the Web Hypertext Application Technology Working Group (WHATWG) “paving the cowpaths” and standardizing common developer behavior. The benefit of standardization is that the data-* prefix allows for predictable programmatic access to the custom attributes. You learn more about that in an upcoming section, but for now, you see the slight difference in the body tag:

<body data-controller=”dashboard” data-action=”render”>

Instead of using CSS classes and IDs, this method maps controllers to actions. This is a very elegant method, especially if you’re building a Ruby on Rails (or similar) application where consistent naming conventions are a high priority. It also allows body IDs and classes to be used for other methods, if the need arises.

The one downside is that leveraging data attributes for section-specific styling is awkward. In a sense, it’s a case of choosing where the one-to-one correspondence will be — on the server with your Ruby on Rails controllers and actions or in your CSS file with section-specific classes and IDs.

Regardless of what specific example you choose, this pattern provides an elegant addition to your toolset, adding in a consistent, scalable, and maintainable method of running code on $(document).ready().

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *