rmurphey Adventures in JavaScript

Dojo Confessions (Or: How I gave up my jQuery Security Blanket and Lived to Tell the Tale)

12 Nov 2009 edit

This is a reprint of an article that originally appeared in the October issue of JSMag.

I recently had the opportunity to architect the front-end of a new web application from scratch, and after years of using jQuery, I decided to give a href="http://dojotoolkit.org"Dojo a try. For a variety of reasons — not least of which was Dojo’s approach to code organization and dependency management — I thought this would be a good project to get some real-world Dojo experience under my belt. What follows is an overview of Dojo from the perspective of an avid jQuery user. I’ve been using jQuery for years. Its simplicity is seductive; after a while, it kind of writes itself. And maybe that was my problem: I was ready to try something new. Many months ago, I wrote on Twitter that I was interested in learning more about Dojo. Rey Bango, jQuery evangelist, wrote back, and put me in touch with a href="http://higginsforpresident.com"Pete Higgins, the Dojo project lead. Pete proceeded to lobby me for months to give his “unified toolkit” a try. I dabbled. I read the docs. Pete plied me with sample code to show me what I was missing, and even drove to North Carolina to lead a Dojo camp. In August, I decided it was time to stop dabbling and dive in. I’d just finished writing some essentially library-less JavaScript for a web-based iPhone application, a task that left me much more confident in my knowledge of JavaScript. With the mobile site out of the way, the client was ready to build the desktop version, and I would be in charge of the front end. It was time to choose a library; this time, I chose Dojo.

Deciding on Dojo

The decision to try a new library on a client project was a tough one — I knew that I could write the basic functionality of the site using jQuery a whole lot faster than I would write it with Dojo, simply because I knew my way around jQuery so much better. Using Dojo would mean I’d be looking up a lot of things, from simple stuff like how to work with DOM selections, to more complicated tasks like how to structure my code for the purposes of reuse and abstraction. As my work on the project progressed and the deadline neared, I had plenty of second thoughts. A few things convinced me to stick with Dojo when the going got tough:

  • Code organization patterns: Dojo provides pretty clear guidance on structuring both your features and your codebase. I’ve given a lot of thought to organizing jQuery code. I wrote an article on the topic for JSMag and gave a presentation on the topic at the jQuery conference. I was eager to try a library that explicitly answers the organization question.
  • Class inheritance: I knew from the start that I was going to use a lot of interaction patterns over and over. I wanted to be able to write those patterns in a way that would let me use them across features while still staying DRY. The class inheritance provided by dojo.declare() was an elegant, easy-to-use solution to the problem.
  • Dependency management: Being able to easily manage dependencies was a huge draw for me; it promotes reuse and abstraction in a big way. Dojo’s dependency management would also pave the way to easily building production-ready files that would combine all the necessary functionality for a given type of page, reducing the number of HTTP requests required.
  • Service method descriptions: This particular application relied on XHRs (AJAX) in a big way. Dojo’s service method description approach would let me manage the URLs and parameters for those requests in a single place, keeping pieces that might change separate from the core code. Eventually, theoretically, the server-side code could actually generate this SMD file automatically. More on this in a bit.
  • Templating: All the XHR responses were JSON, which I’d need to turn into HTML. jQuery has templating plugins to solve this problem, so this wasn’t really a differentiating factor, but nonetheless it was going to make my life easier. I could maintain the templates for turning JSON into HTML separately from my JavaScript, and even programmatically choose the template depending on the situation.
  • The meaning of this: When binding a function to an event on an element in jQuery, this inside the function refers to the element that triggered the event. This is arguably desirable for simple code, but when you start organizing your code into objects with methods and you want this to refer to the object, not the element, it can get painful. The dojo.hitch() method lets you cleanly change the meaning of this for any given function, and it’s transparently rolled into other methods, such as dojo.connect() for event binding.
  • Documentation and support: Dojo has a reputation for poor documentation, and to some extent it’s deserved. Their documentation is a whole lot harder to use than jQuery’s because, at first glance, it’s quite a bit more scattered and substantially more API-based than task-based. However, once I figured out where to look for the docs I needed, finding answers to my questions was pretty painless. I also leaned heavily on some experienced Dojo developers for guidance and support, and dropped in to the ever-helpful #dojo IRC channel on Freenode if I got stuck.

Getting Started

The first step was assembling my new toy. I opted to use the library via Google’s CDN so I could get up and running as quickly as possible. After that, it was time to figure out how I’d organize my files. Dojo actively supports namespaces for components, which means you can put your application files in one or more directories and associate your namespaces with those directories. I created a high-level controller file in the root /js directory; it would be responsible for figuring out which functionality was required for a given page (a decision I’ll eventually revisit). Then, I created a directory inside the root /js directory, where I’d put all of the individual files for the various components.

Finally, I included a line in my controller file to tell Dojo where to find the namespace I’d be using: dojo.registerModulePath('myNamespace', '../js/myNamespace'); Figuring out all of these pieces may have been the hardest part of making the switch to Dojo — it was a whole lot more setup than I was used to with jQuery, and though it is all documented, it took a bit of effort to find the details and to get the paths set up correctly. The time it took to get everything working properly was time that I spent wondering whether I’d made a good decision. Once it was working, it was time to write some actual code and try to answer that question.

Get Some Elements, Do Something With Them

Those words sum up the jQuery paradigm. In jQuery, you query the DOM using a CSS selector, and the result of that query is a jQuery object, which you can then operate on using method chains. It’s fairly rare in jQuery to work directly with a DOM element. While Dojo supports this paradigm through its dojo.query() method and the NodeList it returns, it’s common in Dojo to work directly with a DOM element rather than a NodeList. My initial inclination was to stick with what I knew from jQuery, and to use dojo.query() to get everything I wanted to work with. As I dug in, though, I discovered that it could actually be just as elegant (and less expensive) to work directly with DOM elements, even though they didn’t come with any of the magic of a jQuery object. The syntax for doing so was a bit different — for example, dojo.addClass(myDomElement, ‘foo’) instead of $(myDomElement).addClass(‘foo’) — but the more code I wrote, the more frequently and easily I found myself using the dojo.addClass syntax instead. Embracing this approach was especially valuable when it came to methods that returned something. For example, the dojo.connect() method (used to connect events to elements, similar to $().bind()) returns a connection object, which can be stored and disconnected later without having to know which element the event was attached to. This is, in a word, awesome. It’s also an example of how Dojo requires you to think somewhat differently about how you write your JavaScript.

Returning a Result For the Win

Along those lines, I had to get used to the fact that a lot of Dojo methods returned objects that I could talk to later. For example, dojo.animateProperty() created an animation object which could later be play()’d. All of the XHR methods — and asynchronous methods in general — returned a “deferred” object, to which I could later add success and failure callbacks. jQuery does return the native XHR object from its $.ajax() method, so you can technically add callbacks there too. What I liked about Dojo’s deferred approach is that it provides a common, simple interface for interacting with all asynchronous operations, and even lets you define your own. Getting the hang of how to take advantage of these things took some doing, coming from the more procedural, chained world of jQuery, where just about everything returns a jQuery object. Soon, though, I was setting up animations long before I was ready to play them, and adding callbacks to XHRs after they started.

SMDs: A Unified Way to Talk to the Server

One thing I really wanted to try with Dojo was making use of Service Method Descriptions, or SMDs. An SMD file basically contains information about all of the services provided by a resource. In my case, the resource was the server-side application, which I’d be communicating with to request JSON data. By creating an SMD file, and then instantiating a new Service based on that file, I could create a single place for managing all the paths and parameters I’d use to get what I needed from the server. When I asked the server for something, the Service I created would return a deferred object, to which I could attach callbacks. In the background, Dojo was just running an XHR, but my individual classes didn’t have to worry about the details — I just had to worry about the name of the method and the parameters it required, and the Service I’d defined and instantiated would take care of the rest. For my initial work, I just created the SMD file by hand, but eventually it’s easy to see how the SMD could be generated by the server-side application itself. Here's a sample SMD (normally an SMD would have a lot more services, obviously):

{% codeblock lang:javascript %} { transport : 'GET', envelope : 'PATH', target : '/json',

services : { callouts : { parameters : [ { name : 'callouts', type : 'string' } ], returns : 'object' } } } {% endcodeblock %}

And here's some abbreviated code that makes use of the SMD:

{% codeblock lang:javascript %} cache : {},

services : new dojox.rpc.Service(dojo.moduleUrl('smd', 'json.smd')),

postCreate : function() { // ... },

_lookup : function() { var val = this.domNode.value;

if (this.cache[val]) { this._handleResult(this.cache[val]); } else { var c = this.services.json.callouts({ 'callouts' : val });

c.addCallback(dojo.hitch(this, function(resp) {
  this.cache[val] = resp;

c.addCallback(dojo.hitch(this, '_handleResult'));

} },

_handleResult : function(resp) { // ... } {% endcodeblock %}

Dependency Management and Building

I love jQuery, I do, but this is an area where it is sorely lacking. It offers little guidance on how to structure your codebase, how to ensure that everything you need is loaded, or how to build your code into production-ready files. Dojo really shines here, but again, it takes a bit of getting used to. The base dojo.js includes a ton of basic functionality, but I had to quickly learn to dojo.require() additional functionality as I needed it. In jQuery, you’d do this simply by adding another script tag to your HTML; dojo.require() basically does this for you programmatically, but checks to see that the required code hasn’t already been included first. This means each of your components can require exactly what it needs, and it’s perfectly safe to require the same thing more than once. The flip side of this is that if you forget to require something you need, or if you require it using the wrong name, it’s not always clear exactly where you made the mistake. Each file that will be dojo.require()’d begins with a dojo.provide() statement, telling the dependency management system that it has, indeed, found the right file. After that, the file can go on to dojo.require() its own dependencies, which Dojo loads before proceeding with the rest of the file’s code. The dojo.provide() method also sets up the object chain along the path; for example, if you dojo.provide(‘a.b.c.d’), you can then safely do a.b.c.d.e = { ... }. When it comes time to assemble your individual files into production-ready, combined, minified files, Dojo’s build system is able to parse dojo.require() statements and automatically include the appropriate files; with jQuery, this is a much more manual process that can be difficult to maintain. Creating a build wasn’t as straightforward as I’d hoped it would be, and I stumbed a lot along the way. It took a bit of doing to get all of the paths just right, and to figure out how to have a repeatable build process that we could roll up into our full release process. The payoff was big, though: I could keep my files organized how I wanted them, but only serve one file in production. The build system figured out the steps in between.

Organization, Inheritance and Abstraction

As I mentioned above, code organization has been a big issue for me with jQuery. I’ve developed some patterns that I use to keep my code sane, but plenty of other jQuery developers have not, which can make working with other people’s code rather painful. While it’s certainly possible to write procedural, disorganized code with Dojo, dojo.declare() provides a clear way to avoid it. I talked earlier about creating a namespace for my code and an associated directory. When it came time to start writing, I created individual files in that directory for each component on the page. Inside each file, I indicated which component the file was providing via dojo.provide(), loaded any dependencies via dojo.require(), and then created a class in the namespace using dojo.declare(). The dojo.declare() method takes three arguments: the name of the class you want to create (‘myNamespace.Thinger’), other classes you want to “mix in” to your new class (if any — this argument can be null, a single class, or an array of classes), and, lastly, an object that defines the class methods and properties. The result is a class that can be instantiated using new myNamespace.Thinger(); the object that’s created encapsulates all the behaviors and states associated with a particular Thinger, and you can have as many instances of Thinger as you want. The mixing in thing is huge, because it lets you have a class that incorporates methods defined in another class. For example, I created a class called myNamespace.Toggler that would show either the first item in a list or all of the items in a list; clicking on the first list item would toggle between the behaviors. Once the myNamespace.Toggler class was created, other classes could inherit its behavior simply by passing a reference to the myNamespace.Toggler class as the second argument of dojo.declare(). I was able to encapsulate the Toggler behavior in a reusable way, and keep the code for the classes that inherited the Toggler behavior nice and clean.

Event Management

I mentioned earlier that Dojo has a slightly different take on event binding than jQuery. I should also say that you can bind events to entire NodeLists (the result of dojo.query()) if you want, using .connect() (or convenience methods like .onclick(), etc.). However, if you want a reference to the connection for later use, dojo.connect() is your friend. I created a component using dojo.declare() that was responsible for managing user interaction with a star rating UI element. I used dojo.connect() to hook up some mouseover/mouseout behaviors to the element, and stored the returned connections as properties of the component. When the element was clicked, I registered the rating, and I wanted the mouseover/mouseout behaviors to go away; eliminating them was simply a matter of dojo.disconnect()-ing the stored connections.

Publish and Subscribe

With all of the XHR traffic occurring on the page, I wanted a way to shut it off if the user was inactive for a little while, but I didn’t want to write the code for handling that inside every component that used XHR. Dojo’s pub/sub tools offered the perfect answer. I created a new component whose sole purpose was to watch for inactivity; when it discovered inactivity, it would dojo.publish(‘/user/inactive’). Then, other components — the ones I’d already written, and ones I write in the future — could subscribe to the /user/inactive topic and react accordingly. Pub/sub is an excellent way to allow this sort of abstract communication between components. The component that publishes a “topic” doesn’t care who’s listening; the component that subscribes to a topic doesn’t care which component sent it. It’s another example of how Dojo leads you to think a bit differently about how you architect your applications — knowing about pub/sub can help you write much more loosely coupled code.

What I Missed from jQuery

jQuery’s event delegation-related methods .live() and .is() were hard to live without. There’s a reasonable way to mimic .is(), but no out-of-the-box replacement for .live() — you end up writing your event delegation yourself. Dojo does have dojo.behavior(), which automatically binds event handlers to elements that match a given selector as they’re added to the page; however, the event handlers are bound to individual elements, and there’s no way to provide a context to the selector that you pass to dojo.behavior(). This may be my noob-ness talking, or maybe I’m just used to the error messages I’d see when I did something wrong with jQuery, but I often found myself feeling that the error messages from Dojo were too cryptic. Worse, sometimes I’d do something wrong and it would seem to fail silently. I spent a lot more time in Firebug tracking down the errors of my ways. In general, the thing I really missed from jQuery was the “it just works” aspect of the library. I expect that with time I’ll feel that way about Dojo, but in the meantime there are definitely growing pains. I had to constantly remind myself that the way to solve a problem in Dojo might not be the same way I’d solve it in jQuery. More than once I rewrote a slew of code when I discovered some Dojo methodology or approach I hadn’t known about before.


Lest Rey worry that he never should have introduced me to Pete in the first place, fear not: I don’t expect to give up jQuery anytime soon. If anything, I’m excited to see how the library and the community mature and start answering some of the organization and dependency management questions I mentioned above. As a library, jQuery most definitely has its place; it has virtually no barriers to entry and it has helped usher in an era where it’s dead-simple to create rich, interactive websites. Deciding to use Dojo instead was something of a gamble. I had to convince the project lead that it was a good decision, which was challenging considering the popularity of the jQuery library. He asked lots of pointed questions about the maintainability of the code if I were to leave the project, and those questions were well deserved. If anything, though, I think that choosing Dojo has actually increased the maintainability of the code by presenting clear patterns for organization, abstraction, and dependency management. Did it take a while to come up to speed with Dojo? For sure. Will a jQuery developer off the street be able to jump right in to the code I wrote? Possibly not. At the end of the day, though, it is just JavaScript, and any skilled JavaScript developer should be able to find their way around. They’ll almost certainly find, given an hour or two, that the code I wrote is easier to follow than some of the jQuery code I’ve run into that doesn’t make use of good organizing principles. In the meantime, I hope to be working on the project for a while to come, and I expect the trouble I went through to come up to speed on Dojo will pay big dividends as the application I’m working on grows and matures.

Learn More