Dojo: the JavaScript Toolkit with Industrial-Strength Mojo

 in
Featuring a rich standard library, an extensive collection of turn-key widgets, a unit-testing framework and build tools for minifying your source files, it's no wonder that Dojo is a key part of products from industry giants, such as AOL, Sun Microsystems, BEA and others.
Querying the DOM

The dojo.query function provides a natural segue into general-purpose DOM manipulation and is based on CSS3 selectors. For example, you might use the following logic to query the DOM for any anchor tags and temporarily highlight the background to be yellow for a mouse-over event:

dojo.query("a")
.onmouseover(function(evt) {
  dojo.style(evt.target, {background : "yellow"});
})
.onmouseout(function(evt) {
  dojo.style(evt.target, {background : ""});
});

The statement inside the dojo.addOnLoad block queries the DOM for any anchor tags using the “a” CSS3 selector and returns a collection of nodes as a specialized subclass of Array called dojo.NodeList. Each of the dojo.NodeList methods is then successively applied to the collection of nodes with the final result being returned so that it can be captured into a variable if desired. The dojo.NodeList class provides a number of useful methods, such as addClass, removeClass, style and the various Array functions that you already have seen. For example, if you are seduced by the elegant dot notation that dojo.NodeList provides, you may find yourself with an expression like the following:

// find anchors that are direct descendants of divs
var highlyManipulatedNodes = dojo.query("div > a") 
.addClass("foo")
.removeClass("bar")
.onmouseover(function(evt) { /* ... you ... */})
.map(function(item) { /* ... get ... */})
.filter(function(item) { /* ... the ... */})
.forEach(function(item) { /* ... idea ... */});

It is especially noteworthy that the dojo.NodeList methods named after and triggered by DOM events, such as onmouseover or onblur, accept a single parameter that is a W3C standardized event object, so you are freed from the development and maintenance of yet another layer of subtle incompatibilities when developing a Web application. In fact, the next section investigates the very mechanism that makes this possible.

Event Chaining

It's quite often the case that you'll need to chain together some events arbitrarily to produce an action/reaction effect. The dojo.connect function provides a seamless interface for arbitrarily connecting events and JavaScript Objects. For example, you already know that you could hook up a handler when a user mouses over a specific node by using dojo.query and assigning a function via dojo.NodeList's onmouseover method like so:

dojo.query("#foo") //find the node with id=foo
.onmouseover(function(evt) { /* ... */ });

An alternative implementation via dojo.connect is the following statement, which assembles the connection and returns a handle that can be disconnected later manually if the situation calls for the relationship to be torn down. For example, it's generally a good idea to tear down the handle explicitly before destroying nodes that are involved in the connection:

var h = dojo.connect(dojo.byId("foo"), "onmouseover", function(evt) {
  /* ... use the normalized event object, evt, here ... */
});

/* Later */
dojo.disconnect(h); //tidy up things...

Although the net effect is the same for the two implementations presented, dojo.connect seamlessly allows you to provide Objects as the context. For example, the following variation illustrates how to fire off an event handler whenever a particular function is invoked:

var obj = { // a big hash of functions...
  foo : function() { /* ... */ },
  bar : function() { /* ... */ }
}

// set the handler to fire whenever obj.foo() is run
dojo.connect(obj, "foo", function() {
  /* ... a custom event handler ... */
});

obj.foo(); // the custom handler fires automatically

If you want to use a particular scope with the custom handler, you can wedge it in as a third parameter. The parameters are all normalized internally. Here's how it would work:

var obj1 = { // a big hash of functions...
  foo : function() { /* ... */ },
  bar : function() { /* ... */ }
}

var obj2 = { // a big hash of functions...
  baz : function() { /* ... */ }
}

// fire the handler whenever obj.foo() is run
dojo.connect(obj1, "foo", obj2, "baz");

obj.foo(); // fire obj2.baz right after obj1.foo

Pub/Sub Communication

Although dojo.connect provides a kind of direct action/reaction style of communication, the publish/subscribe metaphor has many highly applicable use cases in loosely coupled architectures in which it's not prudent for objects or widgets to know about one another's existence. This metaphor is simple enough to set up. The dojo.publish function accepts a topic name and an optional Array of parameters that should be passed to any subscribers. Becoming a subscriber to a topic is done through the dojo.subscribe function, which accepts a topic name and a function that is executed in response to the published topic. Here's a working example with a couple Function Objects:

function Foo(topic) {
  this.greet = function() { 
    console.log("Hi, I'm Foo"); 

    /* Foo directly publishes information, 
       but not to anywhere specific... */
    dojo.publish("/lj/salutation");
  }

}

function Bar(topic) {
  this.greet = function() { console.log("Hi, I'm Bar"); }

  / * Bar directly subscribes to information, 
      but not from anywhere specific */
  dojo.subscribe("/lj/salutation", this, "greet");
}

var foo = new Foo();
var bar = new Bar();

foo.talk(); //Hi, I'm Foo...Hi, I'm Bar

A couple variations on the pub/sub metaphor are available, but the vanilla dojo.publish/dojo.subscribe functions relay the general idea. Any situation in which you cannot (for whatever reason) expose an API might be a prime opportunity to take advantage of pub/sub communication in your application.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Shaguf

Anonymous's picture

Hey Guys,
Author of More Servlets and JSP, Marty Hall is coming to Bangalore this April to speak on Choosing an Ajax/JavaScript Toolkit: A Comparison of the Most Popular JavaScript Libraries, Pure Java Ajax: An Overview of GWT 2.0, Integrated Ajax Support in JSF 2.0 and Ajax Support in the Prototype JavaScript Library. You can get more information on developersummit dot com

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState