Part 3: JavaScript Programming With Dojo and Dijit

In addition to being useful in itself, Dijit is a testament to Dojo's solidness and performance. The fact that you can build a really nice, extensible widget framework on top of it shows how cool Dojo is.

But that's only the beginning. Other Dojo-based mini-frameworks like dojo.data and dojox.offline allow you to build data sources and offline storage, respectively, in object oriented fashion. With a bit of JavaScript, you can use these services and many others bundled with Dojo for your own Web 2.0 applications:

Functions Used Everywhere

Before diving headfirst into the dojo pool, you need some basics. The following functions are called in many of the other sections' examples. While none do anything important for the user, per se, they are very handy for the dojo programmer.

Note that some functions, like dojo.connect() are described in more detail in their respective sections. Here, we give you just enough to get started.

dojo.require

dojo.require(String module)

If you've followed through the previous sections, you already know how important dojo.require is. In general if you use a function

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.someModule.someFunction();

You need to include

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.require("dojo.someModule");

If you don't, your scripts will throw a "dojo.someModule not defined" or "dojo.someModule.someFunction not defined..

Dojo's code is split into modules which are similar to packages in Java except that in Dojo a module can contain both constructors (like classes in Java) and simple functions. For example, the "dojo.string" module contains a number of functions, such as dojo.string.substitute(). The "dojo.dnd" module contains a number of constructors such as dojo.dnd.Container dojo.dnd.Source in additon to top-level functions and properties on the dojo.dnd object. We'll learn a lot more about this in Modules

Note the naming convention - functions, properties, and namespace objects start with a lowercase letter, and constructors (which are technically functions but act more like classes) start with a capital letter.

It may seem painful to require all modules, but dojo rewards by:

  • Loading any dependent scripts for you. If dijit.form.TextBox requires dojo.math, you still need only require dijit.form.TextBox.
  • Preventing loading dojo packages twice. dojo.require will simply return if the package is already loaded.
  • Allowing you to build streamlined versions of dojo. If you use dijit.form.TextBox a lot, you can build a custom version of dojo that loads dijit.form.TextBox quickly. Dojo.require() knows whether the function is already loaded, and so you don't have to change any of your code. See The Build System for a discussion.

So you might wonder "So, don't I have to require the dojo module itself to use dojo.require?" Nope. Any function in the top-level package "dojo" is loaded automatically (dojo.query(), dojo.byId(), etc.). These are dojo's Core functions, and represent the most used functions according to usage patterns in the community. This is similar to the Java package java.lang, which is automatically available to all Java programs.

dojo.byId and dijit.byId

dojo.byId(String id)

This function is a synonym for document.all.id in IE or document.getElementById(id) in standard DOM. So you can say:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.byId("breadbox").style.fontSize = "72pt";

If document.getElementById() works in all modern browsers, why use this? Sadly, bugs persist in Internet Explorer which make getElementById() unstable in some common scenarios. Also, dojo.byId() is easier to type.

dijit.byId(String id)

Because of the almost-identical spelling, this method is commonly mixed up with dojo.byId(String id). Here's the difference: dijit.byId() returns a Dijit widget instance - technically, an instance of dijit._Widget or one of its subclasses like dijit.Toolbar or dijit.TreeNode.

So here's the way to remember it:

  • If you need a DOM Node, use dojo.byId. You need a DOM node anytime you call a standard browser function, change a standard property, etc.
  • If you need a widget, use dijit.byId. From it you can access all of the attributes, methods and extension points listed in Part 2.

Or for you who think in terms of functions (e.g. LISP programmers?):

var myDomNode = dojo.byId("foo");
var myWidget = dijit.byId("foo");
console.debug(myDomNode == myWidget.domNode);  // true
console.debug(dijit.byNode(myDomNode) == myWidget); // true

dojo.addOnLoad

dojo.addOnLoad(Function fn)

Sooner or later, every Javascript programmer tries something like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
<script>
  if(dayOfWeek == "Sunday"){
     document.musicPrefs.other.value = "Afrobeat";
  }
</script>
<form name="musicPrefs">
<input type="text" name="other">
...

It doesn't work because the "other" control is not defined yet. You can move the code to the bottom of the page, but that removes the linear nature of HTML. If you're reading the code, you want to zero in on a control, and see the code that influences it close by.

dojo.addOnLoad(...) defers script execution until all the HTML is loaded. So this code:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function setAfrobeat(){
   document.musicPrefs.other.value="Afrobeat";
}
dojo.addOnLoad(setAfrobeat);

conveniently replaces the one above. When the function is small, you may prefer to write it inline:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.addOnLoad(
    function(){
        document.musicPrefs.other.value="Afrobeat";
    }
);

This is the function literal or anonymous function construct of JavaScript. If it looks really, really wierd to you, take a peek ahead at Functions as Variables for an explanation.

dojo.connect

Events in JavaScript or Dojo based applications are essential to making applications work. Connecting an event handler (function) to an element or an object is one of the most common things you will do when developing applications using Dojo. Dojo provides a simple API for connecting events via the dojo.connect() function. One important thing to note here is that events can be mapped to any property or object or element. Using this API you can wire your user interfaces together or allow for your objects to communicate. The dojo.connnect() API does not require that the objects be Dojo based. In other words, you can use this API with your existing code and interfaces.

Below is the code in the tutorial handling events. Here we connected the event handler, helloPressed, to the onclick property of the hello button element. When the button is clicked the funtion helloPressed will be called.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function helloPressed(){
   alert('You pressed the button');
}
function init(){
   button = dojo.byId('helloButton');
    dojo.connect(button, 'onclick', 'helloPressed');
}

It is also possible to use the Dojo event model to connect simple objects. To demonstrate, lets define a simple object with a couple of methods:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var exampleObj = {
    counter: 0,
    foo: function(){
        alert("foo");
        this.counter++;
    },
    bar: function(){
        alert("bar");
        this.counter++;
    }
};

So lets say that I want exampleObj.bar() to get called whenever exampleObj.foo() is called. We can set this up the same way that we do with DOM events:

dojo.connect(exampleObj, "foo", exampleObj, "bar");

Now calling foo() will also call bar(), thereby incrementing the counter twice and alerting "foo" and then "bar". Any caller that was counting on getting the return value from foo() won't be disappointed. The source method should behave just as it always has. On the other hand, since there's no explicit caller for bar(), it's return value will be lost since there's no obvious place to put it.

In either case, each time dojo.connect is called with the same arguments it will result in multiple connections. Later we will discuss strategies on how to guard against this.

Notice that dojo.connect takes a different number of arguments in the examples above. dojo.connect determines the types of positional arguments based on usage.

The Dojo event system allows you to connect to DOM elements or nodes or plain JavaScript objects. The API is sophisticated enough that it allows you to connect multiple listeners to a single object so you can have multiple actions as a result of a single event such as a mouse click. Of course there is an API to disconnect the listeners too. The Events section describes the Dojo Event system in more detail.

dojo.forEach

JavaScript 1.6 has a forEach loop, where you can apply a certain function to each element of an array. Unfortunately at the time of this writing, only Firefox 2 has support for JS 1.6. But never fear! Dojo has defined one you can use in any Dojo-supported browser.

Foreach is syntactic sugar for a regular ol' for loop. So for example:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
for(var i in queueEntries){
   console.debug(queueEntries[i]);
}

Can be written as:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.forEach(queueEntries,
    function(oneEntry, index, array) {
        console.debug(oneEntry + " at index " + index);
    }
);

Like dojo.connect, we use an anonymous function here to define the operation. This function must accept between one and three arguments. The first argument is the value of each value in the array in turn, the second is the current index or position in the array, and the third argument is the array itself.

For this simple loop, forEach isn't anything exciting. But combined with other Dojo functions, especially dojo.query(), it becomes remarkably useful. Consider this snippet, which disables all SELECT tags on the page:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.forEach(
    dojo.query("select", document),
    function(selectTag) {
        selectTag.disabled = true;
    }
);

How cool is that? (Answer: very!) There's no monkeying around with DOM functions, no using tedious names or id's, and it continues to work even when you add new SELECT tags.

Running dojo.forEach on a dojo.query result is so common, that Dojo defines a shortcut. This snippet: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

dojo.query("select", document).forEach(
    function(selectTag) {
        selectTag.disabled = true;
    }
);

does the same thing. But that's not all! New in 1.0 you can collapse the function down to its body, passed in as a string like so: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

// 1.0 only.
dojo.query("select", document).forEach("item.disabled = true;");

Ay carumba! That's a lot of functionality in a tiny 1-line package. Once you get used to the syntax, you'll never want to go back.

There's more on dojo.query in Selecting DOM Nodes with dojo.query

FX and Animations

TBW: Cover Dojo Base animations:

dojo.fadeIn, dojo.fadeOut, dojo.animateProperty, dojo._Animation API

Cover Core animations: (dojo.require('dojo.fx'))

dojo.fx.combine, dojo.fx.chain, dojo.fx.slideTo, dojo.fx.wipeIn, dojo.fx.wipeOut, dojo.fx.Toggler

Direct to dojox.fx animations:

dojox.fx.easing, dojox.fx

Object Orientation

As we saw in the introduction to Part 3, JavaScript doesn't have a class system like Java. But Dojo has functions to simulate one. For some background on JavaScript and prototype-based object orientation, chapter 9 of David Flanagan's JavaScript: The Definitive Guide, 5th edition is a good read.

This section has some pretty abstract stuff, and you may wish to skip it on the first read. Certainly you can do a lot with Dojo without using dojo.declare or the other object orientation functions. But a good knowledge of it will help you program faster and smarter.

Code Re-Use

JavaScript is at its heart an object oriented language, but it is a prototype based object oriented language which does not have the same structure as class based languages like Java.This concept can be hard for new programmers who are not familiar with its construct. Dojo brings the object orientedness into a more familiar domain by modeling concepts that can be followed from Java and letting the toolkit handle the prototyping, inheritance and odd procedures JavaScript requires to make it work. Because of this, it not only allows people to get programming in object oriented JavaScript quicker, but it makes it faster to program because you can let the toolkit handle all of the odd procedures JavaScript requires to make it work.

In dojo, object orientation is used primarily for inheritance. Think of inheritance as "some of the things you can do, I can do better." For example, think of a Progress Bar widget which always displays "nnn%" in the middle. You'd like to change that to "n out of m steps completed." You could, in theory, copy and paste the dijit Progress Bar code to your own file and fix the message. But this is a lot of duplicated code and more Javascript to load into the browser.

In object oriented land, you simply define an object, e.g. NOutOfMProgressBar, as a subclass of ProgressBar. We say NOutOfMProgressBar inherits the behavior of ProgressBar. Then you specify only the method that differs between the two - in this case report() - in NOutOfMProgressBar. Then you can effectively a variable of type NOutOfMProgressBar wherever you'd use a ProgressBar.

This promotes a more effective sharing of code. Dojo can upgrade its version of Progress Bar, and as long as report() is part of the contract, NOutOfMProgressBar will continue to work correctly. And, hey, let's face it. The less Javascript code you have to write, the better off you are.

Authors: David A (?), Craig Riecke

Functions as Variables, or "Here, Do This."

Before jumping into the details, one aspect of Javascript requires some explanation. Unlike Java or C#, functions are first class objects in Javascript. You can do everything with a function that you can do with an integer, including:
  • Assign it to a variable
  • Use it as a value in an array, or associative array
  • Pass it as a parameter
Here's a simple example.  Suppose you want a function that calculates either the maximum or the minimum of two numbers, depending on another parameter to choose.  You can code it like this: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function applyMaxOrMin(a, b, fnName){
   if(fnName == 'max'){
      return Math.max(a,b);
   }else{
      return Math.min(a,b);
   }
}
console.debug(applyMaxOrMin(1, 2, 'max'));

But you can make it a line or two shorter, and more general, by passing a function like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function applyTwoParameterFn(a, b, fn){
   return fn(a,b);
}
console.debug(applyTwoParameterFn(1, 2, Math.max));

Interestingly, you can substitute any function that takes two numbers here, not just Max or Min.

"Ah," you might say, "that's like passing a class in Java and using reflection."  Almost, but not quite.  Java is still strict about type checking, even when calling functions through reflection.  In our example, what if the function passed aren't compatible?  In the example above, what if you pass a function with three variables?  It's handled in The Javascript Way, i.e. sloppily.  If the number of parameters doesn't quite fit, extra ones are lobbed off or null's are added to the end.  Type conversion is applied like it should.

Note: There is no concept of overloading in Javascript!  That's a fundamental difference between it and strongly-typed object languages like Java.

One special function type used a lot in dojo is the callback.  Callbacks are functions that are supposed to be called when processing has ended.  They are especially useful for asynchronous operations like XHR.  You call an asynchronous function and say "call this function when you're done."  If you've used event classes in Java, it's the same concept but looser.

In Java you can define classes anonymously, on-the-fly, right in the middle of a method call.  You can do that in Javascript too.  Simply define the function in the parameter list and omit the name.  For example, instead of defining and passing a new function:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function myTwoParameterFn(a, b) {
   return max(a, -b);
}
console.debug(myTwoParameterFn(1, 2, Math.max));

We can shorten it to:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
console.debug(myTwoParameterFn(
    1, 2,
    function(a,b){
        return max(a, -b)
    }
));

This makes from some pretty strange syntax, especially if the anonymous function is large.  But callbacks are often specified this way when calling dojo functions. 

Declaring a Class

Classes in Dojo are declared with a declare statement with a class name, super-class information, and body. Within the body can be variables and methods.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.declare("ClassName", null, {/*class body*/});

(Note: ClassName is the basic name, but to avoid naming conflicts, use module names like com.coolness.ClassName. See modules for details. For simplicity sake, we will start out with using just the simple name.)

Let's add some more content to our class by giving it a name and showing what the constructor can do. Following is a Person class with a constructor and a moveToNewState() function:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.declare("Person", null, {
        constructor: function(name, age, currentResidence){
                this.name=name;
                this.age=age;
                this.currentResidence=currentResidence;
        },
        moveToNewState: function(newState){
                this.currentResidence=newState;
        }
});

Note the use of anonymous functions here. You are passing to dojo.declare an associative array of anonymous functions. "That's not an anonymous function," you might say, "their names are constructor and moveToNewState!" Strictly speaking, no they aren't. They are anonymous functions with the keys constructor and moveToNewState.

The function named "constructor" is special. It gets called when you create an object with the "new" operator of JavaScript.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
//create an instance of a new person
var matt= new Person('Matt', 25, 'New Mexico');

In pure JavaScript, this is handled by a prototype function named after the class - for example, Person.prototype. Dojo wires in your constructor as a part of the prototype, but then adds extra goodies like calling the superclass constructor and initializing extra properties.

Our Matt object who is 25 currently lives in New Mexico, but let's say he moves a little further west to California. We can set his new currentResidence with the Person class method moveToNewState():

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
matt.moveToNewState('California');

Now the current value of matt.currentResidence shows that he now lives in California.

If you have used prototypes in Javascript, the above example corresponds roughly to:/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function Person(name, age, currentResidence){
        this.name=name;
        this.age=age;
        this.currentResidence=currentResidence;
},
Person.prototype.moveToNewState= function(newState) {
        this.currentResidence=newState;
}
But the dojo version is more compact and has added value as we will see later.

Arrays and Objects as Member Variables

If your class contains arrays or other objects, they should be declared in the constructor so that each instance gets it's own copy. Simple types (literal strings and numbers) and are fine to declare in the class directly.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.declare("my.classes.bar", my.classes.foo, {
        someData: [1, 2, 3, 4], // doesn't do what I want: ends up being static
        numItem : 5, // one per bar
        strItem : "string", // one per bar
         constructor: function() {
                this.someData = [ ]; // better, each bar has it's own array
                this.expensiveResource = new expensiveResource(); // one per bar
        }
});

On the other hand, if you want an object or array to be static (shared between all instances of my.classes.bar), then you should do something like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.declare("my.classes.bar", my.classes.foo, {
        constructor: function() {
                dojo.debug("this is bar object # " + this.statics.counter++);
        },
        statics: { counter: 0, somethingElse: "hello" }
});

"Statics" is not a special dojo construct - you can use any name you want, like "constants". In this example, you'd refer to the variable as myInstance.statics.counter both inside and outside the class definition.

Why is this true for arrays and objects, but not primitives? It's because, like most OOP languages, JavaScript uses object references. For example, given:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
x = { fruit: "apple" };
y = x;

Now x and y both refer to the same object. Modifying x.fruit will also affect y.fruit.

Inheritance

A person can only do so much, so let's create an Employee class that extends the Person class.The second argument in the dojo.declare() function is for extending classes.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.declare("Employee", Person, {
        constructor: function(name, age, currentResidence, position){
                // remember, Person constructor is called automatically
                this.password="";
                this.position=position;
        },
        login: function(){
            if(this.password){
                alert('you have successfully logged in');
            }else{
                alert('please ask the administrator for your password');
            }
        }
});

Dojo handles all of the requirements for setting up the inheritance chain, including calling the superclass constructor automatically. Methods or variables can be overridden by setting the name to the same as it is in the parent class. The Employee class can override the Person class moveToNewState(), perhaps by letting the company pay for moving expenses.

You initialize the subclass the same as the Person class with the new keyword.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var kathryn=new Employee(' Kathryn ', 26, 'Minnesota', 'Designer');

The Employee class passes the first three arguments down to the Person class, and sets the position.Kathryn has access to the login() function found in the Employee class, and also the moveToNewState() function by calling kathryn.moveToNewState("Texas"); Matt on the other hand, does not have access to the Employee login() function.

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
matt.login() // ERROR can't log in because he is not an Employee

Calling Superclass Methods

Often when you're overriding a method, you want to add something to the superclasses method, not totally replace it. Dojo has helper functions to make this easy.

But you don't have to worry in the constructor. As we said above, superclass constructors are always called automatically, and always before the subclass constructor. This convention reduces boilerplate in 90% of cases.

For all other methods, you can use inherited(arguments) to call the superclass method of the same name. Take for example:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
someMethod: function() {
      // call base class someMethod
      this.inherited(arguments);
      // now do something else
    }

Inherited will climb up the scope chain, from superclass to superclass and through mixin classes as well, until it finds "someMethod", then it will invoke that method.

The argument is always literally arguments, a special Javascript array variable which holds all the arguments (like argv in C).

There are a few variations to inherited() for special cases. If you have a method that was put into your object outside of declare, you need to specify the name of the calling function like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
this.inherited("someMethod", arguments);

And you can send custom parameters to the ancestor function. Just place the extra arguments in array literal notation with brackets: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

this.inherited(arguments, [ customArg1, customArg2 ])

Mixins

Just as Dojo adds class-based inheritance to JavaScript, so it adds support for multiple inheritance. We do this through Dojo mixins. The methods and properties of a mixed-in class are simply added to each instance.

In pure object-oriented languages like Java, you must use typecasts to make an object "act like" its mixed-in class (in Java, this is through interfaces). Not in Dojo. You can use the mixed-in properties directly.

Suppose, for example, you have a class called VanillaSoftServe, and classes MandMs and CookieDough. Here's how to make a Blizzard:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.declare("VanillaSoftServe",null, {
    constructor: function() { console.debug ("mixing in Vanilla"); }
});
dojo.declare("MandMs",null, {
    constructor: function() { console.debug("mixing in MandM's"); },
    kind: "plain"
});
dojo.declare("CookieDough",null, {
    chunkSize: "medium"
});
dojo.declare("Blizzard", [VanillaSoftServe, MandMs, CookieDough], {
        constructor: function() {
             console.debug("A blizzard with "+
                 this.kind+" M and Ms and "+
                 this.chunkSize+" chunks of cookie dough."
             );
        }
});

Then the following:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
new Blizzard();

Will first print "mixing in Vanilla" on the debug console because VanillaSoftServe is the superclass of Blizzard. In fact, VanillaSoftServe is the only superclass of Blizzard - the first mixin is always the superclass. Next the constructors of the mixins are called, so "mixing in MandMs" will appear. Then "A blizzard with plain M and Ms and medium chunks of cookie dough." will appear.

Mixins are used a lot in defining Dijit classes, with most classes extending Dijit._Widget and mixing in Dijit._Templated.

Understanding The Parser

One of the most common things to see in a Dojo page is a djConfig block like /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .html4strict .imp {font-weight: bold; color: red;} .html4strict .kw1 {color: #b1b100;} .html4strict .kw2 {color: #000000; font-weight: bold;} .html4strict .kw3 {color: #000066;} .html4strict .coMULTI {color: #808080; font-style: italic;} .html4strict .es0 {color: #000099; font-weight: bold;} .html4strict .br0 {color: #66cc66;} .html4strict .st0 {color: #ff0000;} .html4strict .nu0 {color: #cc66cc;} .html4strict .sc0 {color: #00bbdd;} .html4strict .sc1 {color: #ddbb00;} .html4strict .sc2 {color: #009900;} djConfig="parseOnLoad: true" and later /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .javascript .imp {font-weight: bold; color: red;} .javascript .kw1 {color: #000066; font-weight: bold;} .javascript .kw2 {color: #003366; font-weight: bold;} .javascript .kw3 {color: #000066;} .javascript .co1 {color: #009900; font-style: italic;} .javascript .coMULTI {color: #009900; font-style: italic;} .javascript .es0 {color: #000099; font-weight: bold;} .javascript .br0 {color: #66cc66;} .javascript .st0 {color: #3366CC;} .javascript .nu0 {color: #CC0000;} .javascript .me1 {color: #006600;} .javascript .re0 {color: #0066FF;} dojo.require("dojo.parser");. Together these lines include and enable Dojo's page parsing infrastructure. This machinery layers on top of dojo.query() to provide a way to declare instances of any class via markup in your page. To understand what we mean by that, let's take a simple example page which consists of a single tree backed by a JSON data store:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"
        djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
        dojo.require("dojo.data.JsonItemStore");
        dojo.require("dijit.Tree");
        dojo.require("dojo.parser");
        var countries = new dojo.data.JsonItemStore({ url: "countries.json" });
</script>
</head>
<body class="tundra">
        <div dojoType="dijit.Tree" store="countries" labelAttr="name" typeAttr="type"
           query="{type:'continent'}" >
</div>

In this case, we see the familiar use of the dojoType attribute to denote where an instance of our widget should be created. This is the functional equivalent of writing:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"
       djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
        dojo.require("dojo.data.JsonItemStore");
        dojo.require("dijit.Tree");
        dojo.require("dojo.parser");
    dojo.addOnLoad(
        var countries = new dojo.data.JsonItemStore({ url: "countries.json" });
        var tree = new dijit.Tree({
            store: countries,
            labelAttr: "name",
            typeAttr: "type",
            query: {type: "continent"}
        }, dojo.byId("treePlaceHolder"));
    });
</script>
</head>
<body class="tundra">
        <div id="treePlaceHolder"></div>

In fact, they're identical. The only difference is that in the first example, the work of locating and creating the widget instance is handed off to the Dojo parser. Fundamentally, this means that the parser is:

  • locating the nodes with dojoType attributes in the page
  • taking the attributes assigned to them and passing them into the constructor as properties on the configuration object
  • passing the source node for the widget as the second parameter to the constructor

There's nothing about this process (except perhaps the passing of the node) which should be specific to widgets, and indeed the Dojo parser is equipped to create instances of any class. That means that we can revise our first example to be fully markup-driven:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"
        djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
        dojo.require("dojo.data.JsonItemStore");
        dojo.require("dijit.Tree");
        dojo.require("dojo.parser");
</script>
</head>
<body class="tundra">
        <div dojoType="dojo.data.JsonItemStore" url="countries.json" jsId="countries"></div>
        <div dojoType="dijit.Tree" store="countries" labelAttr="name" typeAttr="type"
           query="{type:'continent'}" >
</div>

So how does this work? What happens to the source node in the resulting page when what we're creating isn't a widget? And what about the seemingly magical properties dojoType and jsId?

The Parsing Algorithm

To fully understand the parser, it's important to understand it's operation in broad terms. The parser operates by:

  • Searching the document for elements with a dojoType attribute. This search is linear and nodes are returned in document order.
  • Iterating over all matching nodes, attempting to match the declared type with an available class to instantiate.
    • If a class is found, the parser iterates over attributes of the class's prototype and populates the arguments object from the values of attributes on the source node of the same name. Lightweight type conversion is performed.
    • If a markup factory is found for the class, it is used to create a new instance to return
    • If no markup factory is found, the class is constructed using the new operator. The arguments to the constructor are assumed to be in the form /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .javascript .imp {font-weight: bold; color: red;} .javascript .kw1 {color: #000066; font-weight: bold;} .javascript .kw2 {color: #003366; font-weight: bold;} .javascript .kw3 {color: #000066;} .javascript .co1 {color: #009900; font-style: italic;} .javascript .coMULTI {color: #009900; font-style: italic;} .javascript .es0 {color: #000099; font-weight: bold;} .javascript .br0 {color: #66cc66;} .javascript .st0 {color: #3366CC;} .javascript .nu0 {color: #CC0000;} .javascript .me1 {color: #006600;} .javascript .re0 {color: #0066FF;} new someClass(argumentsObj, sourceNode);
  • Events from markup are attached (although some may have been handled earlier)

The above process alludes to some features of the parser which we haven't seen in action yet. Here's a more sophisticated example. We create a class called "example.Class". The parser runs and creates an instance of this class (the div dojoType="example.Class"), which you can then access through the global variable "thinger."

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"
        djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
        dojo.require("dojo.parser");
        dojo.declare("example.Class", null, {
                constructor: function(args, node){
                        //  this class constructor is designed for the
                        // parser's "args, node" convention
                        dojo.mixin(this, args);
                },
        });
</script>
</head>
<body class="tundra">
        <div dojoType="example.Class" foo="bar" jsId="thinger">
                <script type="dojo/method">
                        // this block is executed as the class is created but
                        // after the class constructor is finished
                        console.debug(this.foo);   // Prints "bar"
                </script>
        </div>

Each script of type "dojo/method" is executed after the constructor runs. We saw examples of this in Part 1, Example 2.. Finally, the class constructor uses a mixin to copy the attributes from tag to properties in the instance. Thus thinger is created by calling the constructor with args = {foo: "bar"} and node as the div node itself. Foo is created as a property by mixin.

Type Conversions

Lightweight type conversions are done on the attribute values. The types are based on the property types used in the definition of the class. For example:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"
        djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
        dojo.require("dojo.parser");
        dojo.declare("example.Class", null, {
                constructor: function(args, node){
                        //  this class constructor is designed for the
                        // parser's "args, node" convention
                        dojo.mixin(this, args);
                },
                title: "Hello",
                isEnabled: true,
                dayCount: 45,
                onClick: function(){},
                names: ["Monday", "Tuesday", "Wednesday"],
                startDate: new Date()
        });
</script>
</head>
<body class="tundra">
        <div dojoType="example.Class" title="Good Morning" isEnabled="false" dayCount="4" onClick="alert(thinger.dayCount)" names="Thursday, Friday" startDate="2008-01-01" jsId="thinger">
                <script type="dojo/method">
                        // this block is executed as the class is created but
                        // after the class constructor is finished
                        console.debug(this.foo);   // Prints "bar"
                </script>
        </div>

In this example, the attributes will be converted to their correponding types that were used in the definition of example.Class:

  • title: String
  • isEnabled: Boolean
  • dayCount: Number
  • onClick: Function. Specify the function body in the attribute.
  • names: Array. Separate the array members by commas. Array members are assumed to be strings.
  • startDate: Date. "now" can be used to get the current date, otherwise, dojo.date.stamp.fromISOString() will be used to convert the text string to a Date object.

If the property type does not match one of the types listed above, then dojo.fromJson() will be used to convert the attribute value.

Markup Factory

If the class declares a method with the name markupFactory, that function will be used to create the object instance, instead of the constructor. This is useful if the class has special initialization for instances created via markup, versus instances created in script via the class constructor. An example class that defines a markupFactory method:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}
<script type="text/javascript" src="http://o.aolcdn.com/dojo/0.9.0/dojo/dojo.xd.js"
        djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
        dojo.require("dojo.parser");
        dojo.declare("example.Class", null, {
                constructor: function(args, node){
                        //  this class constructor is designed for the
                        // parser's "args, node" convention
                        dojo.mixin(this, args);
                },
                title: "Hello",
                isEnabled: true,
                dayCount: 45,
                onClick: function(){},
                names: ["Monday", "Tuesday", "Wednesday"],
                startDate: new Date(),
                markupFactory: function(params, domNode, constructorFunction){
                        //params: object that contains the markup attribute values,
                        //with type conversion already completed.
                        //domNode: the DOM node (the div in the code below)
                        //constructorFunction: The constructor function matching
                        //the dojoType in markup. In this example, example.Class
                        var instance = new constructorFunction(params, domNode);
                        //Do special initialization intialization here
                        return instance;
                }
        });
</script>
</head>
<body class="tundra">
        <div dojoType="example.Class" title="Good Morning" isEnabled="false" dayCount="4" onClick="alert(thinger.dayCount)" names="Thursday, Friday" startDate="2008-01-01" jsId="thinger">
                <script type="dojo/method">
                        // this block is executed as the class is created but
                        // after the class constructor is finished
                        console.debug(this.foo);   // Prints "bar"
                </script>
        </div>

Modules

Once you get started writing Dojo code, you'll start writing more sophisticated client side code. And the more code you write, the more you will split into JavaScript modules. The more files, the more dangerous are the consequences of using JavaScript carelessly. For example, a harmless little function like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
function avg(listOfNumbers) {
   sum = 0;
   for(i in listOfNumbers){
      sum += listOfNumbers[i];
   }
   return sum / listOfNumbers.length;
}

tucked away in a script will wreak havoc on the following in another script: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

function sum(listOfNumbers) {
   var retval = 0;
   for(i in listOfNumbers){
       retval += listOfNumbers[i];
   }
   return retval;
}
document.print(avg(a));
document.print(sum(a));

Because the variable sum in the avg function is stored in the global namespace (i.e. it is not prefixed by "var"), it will overwrite the loaded sum() function in the second script. This leads to hard-to-diagnose errors

What you need is to keep these modules corralled in their own spaces. While modularization is not part of JavaScript, Dojo can simulate it for you through namespaces. And you've been using them all along! Whenever you execute a dojo.require, you are invoking a module loaded into its own namespace. So, for example, dijit.Toolbar is a namespace. Java and C# have similar constructs named packages.

While namespaces are optional, they tend to lessen hassles down the road as your code base grows.

What dojo.require Does

The modules bundled with Dojo correspond roughly to .js files underneath the Dojo root. Looking underneath your dojo root directory, you will see at least three top-level directories dojo. dijit. and dojox. Everything that you've dojo.require'd so far has begun with one of these three prefixes.

Like most modern languages, Dojo uses "." to separate modules from submodules from sub-submodules, etc. These correspond to directories, subdirectories and sub-subdirectories underneath the Dojo root. The name after the last period is the JavaScript file name. So for example:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.require("dijit.form.Button");
// Esentially loads the script dijit/form/Button.js underneath the Dojo root

Why not just load the scripts with a SCRIPT tag? Well, besides being shorter, a dojo.require statement ensures that modules are not loaded twice. They also make the build system function better, which you'll see in The Build System chapter. You can, however, pull in any Dojo code after the initial dojo.js script tag with a script tag and the package system will ensure that dependencies for that package are still satisfied. This trick is particularly handy when you need line-number debugging information from some new chunk of code you're working on.

The script dijit/form/Button.js in turn starts like this:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.provide("dijit.form.Button");
dojo.require("dijit.form._FormWidget");
dojo.require("dijit._Container");
dojo.declare("dijit.form.Button", dijit.form._FormWidget, {
   // [snip]
}
dojo.declare("dijit.form.DropDownButton", [dijit.form.Button, dijit._Container], {
   // [snip]

All of the classes defined in this script are then available to you. By convention, most modules define one class named after the module itself, as in our dijit.form.Button example. You can also use dijit.form.DropDownButton, if you dojo.require("dijit.form.Button"). But dojo.require("dijit.form.DropDownButton") will not work. Each script provides only one package.

Note, however, that it's Dojo convention to prefix "suggested private" classes with a "_". You shouldn't use these classes, even though JavaScript won't throw an error if you do.

Creating Your Own Modules

Easy Way: A Dojo Peer Directory

So suppose you want to create your own module called explosive.space.Modulator. The most straightforward method involves creating dojoroot/explosive/space/Modulator. That way, "explosive" is at the same directory level as "dojo" and "dijit":

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw3 {color: #000066;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .re0 {color: #0000ff;} .geshifilter .re1 {color: #0000ff;} .geshifilter .re2 {color: #0000ff;} .geshifilter .re3 {color: #808080; font-style: italic;} .geshifilter .re4 {color: #0000ff;}
[criecke@smoochie js]$ ls
dijit  dojo  dojox  util
[criecke@smoochie js]$ mkdir --parents explosive/space
[criecke@smoochie js]$ ls
dijit  dojo  dojox  explosive  util

Now create the file "explosive/space/Modulator.js":

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.provide("explosive.space.Modulator");
dojo.declare("explosive.space.Modulator",null,{
    // fil in the body here
});

And you're ready to include it like any other Dojo module:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.require("explosive.space.Modulator");
var eludiumFuel36 = new explosive.space.Modulator();

Sometimes instead of classes, you may want to define plain ol' functions in a module. That's fine too. For example: you could define explosive.space.utilities:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.provide("explosive.space.utilities");
explosive.space.utilities.shuffleOverToCannon = function(steps) {
   // body here
}

Then, after the dojo.require, you can call explosive.space.utilities.shuffleOverToCannon().

Cleaner Way: External Directories

The problem with this approach is your mucking up the dojo root directory. It's better to keep your development separate from Dojo itself. It tends to make source control easier to deal with.

So let's say you put "explosive/space/Modulator.js" underneath private_dojo at the same level as Dojo root. Then you only need to add the registerModulePath statement:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dojo.registerModulePath("explosive","../../private_dojo/explosive");
dojo.require("explosive.space.Modulator");

The module path is specified relative to the /dojoroot/dojo directory.

Enterprise-Class Way: Custom Builds

An alternate method, which doesn't require the registerModulePath, is to have a build script (Ant, Makefile, or whatever) mix both Dojo and your custom stuff under the same root. That way your source code stays separate from Dojo's in development, but mixes together in a nice way for production. See Custom Builds for details.

Module Helpers

Modules for Non-JavaScript Resources

dojo.require() works fine for JavaScript, but what about images, CSS, and other resources? Do you need to access them through absolute paths? Fortunately no. The following code:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var imgNode = document.createElement("img");
imgNode.src = dojo.moduleUrl("explosive.space","images/kaboom.gif");
locates the kaboom.gif file in our module. The nice thing is ... this code will work in any page that has registered the module. It doesn't matter where the code snippet is located relative to kaboom.gif. Slick!

Conditional Inclusion

Sometime modules are dojo.require'd because they may be used. If they are not used, that's a small bit of wasted time. If you know at runtime whether modules need inclusion, you can use dojo.requireIf: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

dojo.requireIf(dojo.isIE, "explosive.space.BlueScreenOfDeathCatcher");

Creating and Scripting Widgets

Part 2: Dijit discussed how to work with widgets declaratively, using the "dojoType" attribute in HTML tags. This section explains how to create and interact with dojo widgets programmatically, via JavaScript.

This discussion spans the following topics:

Creating a Widget Programmatically
How to create dojo widgets from JavaScript, and swap/insert them into a page's DOM existing structure
Interacting With Widgets
How to change properties/behavior of a dojo widget throughout the lifecycle of a page
Writing Your Own Widget
How to write and instantiate your own widgets

Creating a Widget Programmatically

In Part 2: Dijit, you saw how to instantiate (create an instance of) a dojo widget declaratively, using the "dojoType" attribute: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}

<div dojoType="dijit.TitlePane" title="Outer Pane">
   This is a title pane containing another title pane
   <div dojoType="dijit.TitlePane" title="Inner Pane">
      And this is the inner title pane...
   </div>
</div>

While this method is very convenient, widgets declared in this way are instantiated only when the page first loads. What if, however, you want to create a widget at some later time, e.g. as the result of a user-action? This is one case where we would want to instantiate a widget programmatically, which looks like: /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}

var button1 = new dijit.form.Button(params, srcNodeRef);

Let's pick that programmatic example apart:

/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
var button1
This is the name of the variable that will refer to the instantiated widget. You can use this variable to interact with the widget later (see Interacting With Widgets)
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #000066; font-weight: bold;} .geshifilter .kw2 {color: #003366; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .co1 {color: #009900; font-style: italic;} .geshifilter .coMULTI {color: #009900; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #3366CC;} .geshifilter .nu0 {color: #CC0000;} .geshifilter .me1 {color: #006600;} .geshifilter .re0 {color: #0066FF;}
dijit.form.Button
This is the fully-qualified name of the widget you want to instantiate; freeing you to, perhaps, later instantiate /* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .javascript .imp {font-weight: bold; color: red;} .javascript .kw1 {color: #000066; font-weight: bold;} .javascript .kw2 {color: #003366; font-weight: bold;} .javascript .kw3 {color: #000066;} .javascript .co1 {color: #009900; font-style: italic;} .javascript .coMULTI {color: #009900; font-style: italic;} .javascript .es0 {color: #000099; font-weight: bold;} .javascript .br0 {color: #66cc66;} .javascript .st0 {color: #3366CC;} .javascript .nu0 {color: #CC0000;} .javascript .me1 {color: #006600;} .javascript .re0 {color: #0066FF;} yourmodule.form.Button (see Writing Your Own Widget Class)
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .i