The Event System

Dojo's event system offers a refreshing alternative to the normal JavaScript events. With Dojo, you connect functions to one another, creating a link that calls one function when another fires. This means that you can connect a function of your own to

  • a DOM event, such as when a link is clicked;
  • an event of an object, such as an animation starting or stopping;
  • a function call of your own, such as bar();
  • a topic, which acts as a queue that other objects can publish objects to.

Your connected function is called when the event occurs. With simple events, when it calls your function, dojo passes your function a normalized event object, so that it can respond correctly, responding to keystrokes or stopping default behavior. With topics, Dojo passes any subscribed functions the object that was published. Dojo happily abstracts away all of the difficulty of cross-browser event systems, offering programmers a coherent event system that acts consistently across browsers.

Dojo's event system is flexible and gives you a few options for connecting your functions. In the core package, you have both simple events (which use a signal and slot system, similar to Qt's) and topics. In this section, you'll learn the following:

  • how to connect functions to one another with dojo.connect,
  • what comes with an event object
  • how to connect functions with topics and even publish your own objects to the topic
  • how to enjoy event-based programming

Simple Connections with dojo.connect

Dojo provides a pair of functions to handle many of your event-handling needs: dojo.connect and dojo.disconnect. With dojo.connect, you can link one function to fire when another does, handling DOM events and custom functions with a single mechanism. Additionally, dojo.connect allows you to connect as many functions to an event as necessary. With dojo.disconnect, you can cancel a connection, assuming you've kept some reference to it.

How does it work?

Imagine that you're hungry and have decided to cook a pizza in your oven. The pizza will take 17 minutes, so you set a timer. You have better things to do than sit around your kitchen hanging out by the timer though, so you get your brother and tell him, "When you hear the oven timer, take the pizza out of the oven and bring me a slice." Your brother can only keep track of one thing at a time, and you don't want your house to burn down, so you tell your sister, "When you hear the over timer, turn off the oven." Because you're a little worried that your dirty oven might start to smoke, you tell them both, "If you hear the smoke alarm, come get me and then go outside." After you get your pizza, you tell your brother and sister that they don't have to worry about the oven alarm now and that they can go play until you call for them again. You then set the oven alarm to wake you up from a nap.

In this example, your siblings are functions. Your telling them to respond to certain events, such as "onPizzaDone" and "onHouseOnFire" performs the same function as dojo.connect — it sets up your siblings (functions) to listen for an event and perform their tasks when they receive notice. The various alarms are similar to event objects; they inform your siblings of important details about the situation (such as what is beeping). Telling your siblings that they don't need to worry about the oven alarm anymore is similar to dojo.disconnect; the next time the oven alarm goes off, it means that you need to wake up, and you don't want your brother hunting for a pizza needlessly, so you've told him to stop listening to that event.

Syntax

Dojo.connect takes a variety of forms of arguments, depending on how you are planning to use it. This section will cover those various forms, based on use cases for them. You can think of it as a more in-depth version of the overview from Functions Used Everywhere.

Dojo.connect has the following signature (acceptable types in square brackets):

handle = dojo.connect(Scope of Event [object or null], Event [string], Context of Linked Method [string or null], Linked Method [string or function], Don't Fix Flag [boolean])

All of the options for calling dojo.event are explored further below.

Example Code for Reference

/* 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;}
<head>
<title>Dojo Events are Great</title>
<script src="dojo/dojo.js" type="text/javascript"></script>
<script type="text/javascript">
function foo() { console.debug("A click upon your houses!"); }
function globalGuy() { console.debug("Global Guy fired!"); }
var someObject = {
   bar: function() { console.debug("Bar fired!"); return 7; },
   baz: function() { console.debug("Baz fired!"); return 14; }
}
var anotherObject = {
    afterBaz: function () { console.debug("afterBaz fired!"); }
}
</script>
<body>
<a id="firstLink" href="http://dojotoolkit.org/">Dojo</a> is an excellent tool kit.
</body>

Connecting to a DOM Event

To connect a function to a DOM event with Dojo, you first need to get the node that you want to connect to. Here, I'll use the venerable dojo.byId.

/* 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;}
firstLinkNode = dojo.byId("firstLink");
Now, to fire foo when a user clicks #firstLink, and I have the node, so I just need to use dojo.connect for the heavy lifting. /* 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;}
firstLinkConnections = [];
firstLinkConnections[0] = dojo.connect(firstLinkNode, 'onclick', foo);

In this example, I passed dojo.connect the object I want my function to listen to (in this case, a DOM node), the name of the function that should trigger my function's call (in this case, the "onclick" event), and the name of my function. Note that I keep a reference to the connection by setting firstLinkConnections[0] to the return value of dojo.connect. This will allow me to disconnect the listener later, if I desire. Now, when a user clicks "Dojo," a message appears in the log Because my function is global in scope, I can pass it directly to connect. The following, however, are equivalent:

/* 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;}
firstLinkConnections[0] = dojo.connect(firstLinkNode, 'onclick', null, foo);

and

/* 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;}
firstLinkConnections[0] = dojo.connect(firstLinkNode, 'onclick', null, "foo");

Now, if I also want to connect someObject.bar() to #firstLink, I can do that.

/* 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;}
firstLinkConnections[1] = dojo.connect(firstLinkNode, 'onclick', someObject, "bar");

Because I've used Dojo's event handling, I can connect an arbitrary number of functions to fire on an event.

A note about the event names: In most cases, event names now are lower case, except in special cases (e.g., some Mozilla DOM events). Dojo will add "on" to your event name if you leave it off (e.g., 'click' and 'onclick' are the same thing to dojo). Dojo responds to all of the usual events (e.g., onclick, onmouseover, etc.) and the onkeypress event for capturing typing.

A note about return values: Any value returned by a function called by dojo.connect will be lost.

Connecting Functions to One Another

Connecting functions to one another is even simpler than connecting them to DOM events; because you already have a reference to the function, you don't need to do any byId or query work. To have anotherObject.afterBaz fire after someObject.baz fires, use 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;}
objectConnections = [];
objectConnections[0] = dojo.connect(someObject, "baz", anotherObject, "afterBaz");

In the above code, the first argument is the context of "baz," the second argument is the event (in this case, when baz fires), the third argument is the context of your listener function, and the fourth argument is the listener function itself. Connecting two global functions is even easier:

/* 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;}
objectConnections[1] = dojo.connect(foo, globalGuy);

Now, whenever foo is called, globalGuy will also fire. As you might expect, connecting a method to a global function, or vice versa, is logical and simple:

/* 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;}
objectConnections[2] = dojo.connect(foo, anotherObject, "afterBaz");
objectConnections[3] = dojo.connect(someObject, "baz", globalGuy);

Disconnecting

To disconnect listeners from events, you simply pass the connection handle (the return value of dojo.connect to dojo.disconnect. To disconnect globalGuy from someObject.baz, I use 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;}
dojo.disconnect(objectConnections[3]);

Event Object

When you connect a function to a DOM event with dojo.connect, dojo passes your function a normalized event object. This means that, regardless of the client's browser, you can count on a set of standard attributes about the event and a set of methods to manipulate the event.

Syntax

Assume that your function has been called by dojo.connect and takes an argument named event

Dojo provides the following attributes with an event object:

  • event.target — the element that generated the event
  • event.currentTarget — the current target
  • event.layerX — the x coordinate, relative to the event.currentTarget
  • event.layerY — the y coordinate, relative to the event.currentTarget
  • event.pageX — the x coordinate, relative to the view port
  • event.pageY — the y coordinate, relative to the view port
  • event.relatedTarget — For onmouseover and onmouseout, the object that the mouse pointer is moving to or out of
  • event.charCode — For keypress events, the character code of the key pressed

Dojo provides the following methods with an event object:

  • event.preventDefault — prevent an event's default behavior (e.g., a link from loading a new page)
  • event.stopPropagation — prevent an event from triggering a parent node's event

Additionally, dojo.stopEvent(event) will prevent both default behavior any any propagation (bubbling) of an event.

Example Code for Reference

/* 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;}
<head>
<title>Dojo Events are Great</title>
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.0.0/dojo/dojo.xd.js"></script>
<script type="text/javascript">
function echo(event) {
        key = event.charCode;
        console.debug(event.charCode);
}
function foo(event) {
        dojo.stopEvent(event);
        console.debug("The link was clicked");
}
dojo.addOnLoad(function() {
        interactiveNode = dojo.byId("interactive");
        linkNode = dojo.byId("link");
        dojo.connect(interactiveNode, 'onkeypress', echo);
        dojo.connect(linkNode, 'onclick', foo);
});
</script>
<body>
        <a href="http://dojotoolkit.org" id="link">Dojo</a> is great.
        <form>
                <label for="infield"> Type some text: </label>
                <input id="interactive" type="text" name="infield">
        </form>
</body>

Using a Dojo Event Object

In the example code, we have two functions that are connected to two different events. Echo sends the key code of any key typed in the text input field to the console. It does so by using the charCode property of the normalized event object. Foo is connected to the #link and cause it to send "The link was clicked" to the console instead of changing the browser's location; by using the preventDefault method of the normalized event object, connections to change the default behavior of DOM objects.

Now, imagine that you want to detect for the down arrow key in the text box. To do this, we just need to attach a new event listener to the text box and check to see if each keycode is the keycode for the down arrow. And how do you know what the keycode for the down arrow is, you may ask? Well, Dojo provides constants for every non-alpha-numeric key (see: Key code constants). In our case, we are interested in dojo.keys.DOWN_ARROW. So, assuming that you want to just log when the down arrow is pressed, the following code should do the job: /* 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.connect(interactiveNode, 'onkeypress', function (evt) {
        key = evt.keyCode;
        if(key == dojo.keys.DOWN_ARROW) {
                console.debug("The user pressed the down arrow!");
        }
});

Key Code Constants

Dojo provides the following keycode constants in the namespace dojo.keys:

  • BACKSPACE
  • TAB
  • CLEAR
  • ENTER
  • SHIFT
  • CTRL
  • ALT
  • PAUSE
  • CAPS_LOCK
  • ESCAPE
  • SPACE
  • PAGE_UP
  • PAGE_DOWN
  • END
  • HOME
  • LEFT_ARROW
  • UP_ARROW
  • RIGHT_ARROW
  • DOWN_ARROW
  • INSERT
  • DELETE
  • HELP
  • LEFT_WINDOW
  • RIGHT_WINDOW
  • SELECT
  • NUMPAD_0
  • NUMPAD_1
  • NUMPAD_2
  • NUMPAD_3
  • NUMPAD_4
  • NUMPAD_5
  • NUMPAD_6
  • NUMPAD_7
  • NUMPAD_8
  • NUMPAD_9
  • NUMPAD_MULTIPLY
  • NUMPAD_PLUS
  • NUMPAD_ENTER
  • NUMPAD_MINUS
  • NUMPAD_PERIOD
  • NUMPAD_DIVIDE
  • F1
  • F2
  • F3
  • F4
  • F5
  • F6
  • F7
  • F8
  • F9
  • F10
  • F11
  • F12
  • F13
  • F14
  • F15
  • NUM_LOCK
  • SCROLL_LOCK

Page Load / Unload

To perform any DOM scripting, one needs a DOM to work with. Otherwise, you don't have nodes to attach your events to or manipulate, and the results of your program can be undesirable (e.g., no output for no obvious reason, or events attached to the document root).

Dojo elegantly solves this problem with dojo.addOnLoad, which we introduced back in Functions Used Everywhere. Better yet, dojo.addOnLoad starts your scripts after the DOM has loaded but before all of the page elements have loaded. That means your script doesn't have to wait for images and other large resources before it manipulates page structure. This can significantly improve the perceived performance of your scripts.

Like other event handlers, you register a function with addOnLoad - either a function literal, or a function name as a string. You can also pass an object and a method name (as a string) to call an object-oriented 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;}
// Runs tp.toggle() - see dijit.TitlePane - when the page is done loading.
   dojo.addOnLoad(tp, "toggle");
   // Function form
   dojo.addOnLoad(function() {
      dojo.byId("Status").innerHTML = "You may begin.";
   });
<script>
<body class="tundra">
   <div dojoType="dijit.TitlePane" jsId="tp"></div>
   <div id="Status"></div>

Acting in parallel to dojo.addOnLoad is dojo.addOnUnload , which runs functions when the page is being "unloaded" (e.g., when the user clicks a link off of the current page). This gives you the opportunity to send notifications to your web application or clean up unavoidable memory leaks.

dojo.addOnLoad and Cross-Domain Loading

Dojo.addOnLoad() is doubly-important when using cross-domain resources. Non-DOM-related Dojo functions can be called anytime from a locally-installed Dojo build, as in:

/* 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>
   // Works in local Dojo.  Doesn't work in XDomain
   dojo.require("dojox.uuid.Uuid");
   dojo.require("dojox.uuid.generateTimeBasedUuid");
   var uniqueId = new dojox.uuid.Uuid(dojox.uuid.generateTimeBasedUuid());
   ...

In a local Dojo build, dojo.require loads the dojox.uuid code right away. In Cross-Domain Dojo, this is not guaranteed due to browser security rules. But this code works fine on either:

/* 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>
   // Works in local and XDomain
   dojo.require("dojox.uuid.Uuid");
   dojo.require("dojox.uuid.generateTimeBasedUuid");
   dojo.addOnLoad(function() {
       var uniqueId = new dojox.uuid.Uuid(dojox.uuid.generateTimeBasedUuid());
       ...
   });
</script>

We highly recommend initializing your code with dojo.addOnLoad. Semantically, it's no different than bare code, and it allows you to switch from XDomain to local Dojo and back.

Publish and Subscribe Events

In addition to the simple event system created by dojo.connect, dojo offers support for anonymous publication and subscription of objects, via dojo.publish and dojo.subcribe. These methods allow a function to broadcast objects to any other function that has subscribed. This is dojo's topic system, and it makes it very easy to allow separate components to communicate without explicit knowledge of one another's internals.

There are three functions that you need to understand to use dojo's topic system: dojo.publish, dojo.subscribe, and dojo.unsubscribe Dojo.publish calls any functions that are connected to the topic via dojo.subscribe, passing to those subscribed functions arguments that are published (see syntax for details). As one might expect, dojo.unsubscribe will cause a previously subscribed function to no longer be called when dojo.publish is called in the future

How does it work?

Imagine that you run a running a conference, and there will be updates throughout the day. You could collect contact information for everyone at the beginning of the day, along with each person's interests. However, this would be a lot of logistical work. Instead, you decide to use your facility's Public Address System. When there is an update to the schedule, you announce "This is an update to the schedule: the Dojo training is full and we have added yet a third time slot for it tomorrow." When there is meal information, you announce "This is an update about food: we will be serving free ice cream in the main hall in five minutes." This way, anyone interested in your information can pay attention to any updates that could change their behavior. You don't need to know who is subscribing, and they don't need to fill out a bunch of paper work — it's a win-win.

Syntax

dojo.publish

/* 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.publish(Topic Name [string], Arguments to Pass to Subscribed Function [array])

dojo.subscribe

/* 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;} handle = dojo.subscribe(Topic Name [string], Context of Linked Method [string or null], Linked Method [string or function])

dojo.unsubscribe

/* 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.unsubscribe(Handle [handle object])

Example Code for Reference

/* 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 globalGuy(arg) { console.debug("Global Guy fired with arg " + arg); }
var someObject = {
   bar: function(first, second) { console.debug("Bar fired with first of "+first+" and second of "+second); return 7; },
}
}

Subscribing and Publishing Topics

To connect globalGuy to the topic "globalEvents" and someObject.bar to "fullNames", you simply use dojo.subscribe, as follows:

/* 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;}
topics = [];
topics[0] = dojo.subscribe("globalEvents", null, globalGuy);
topics[1] = dojo.subscribe("fullNames", "someObject", bar);

Note that the following alternative form would also work:

/* 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;}
topics = [];
topics[0] = dojo.subscribe("globalEvents", globalGuy);
topics[1] = dojo.subscribe("fullNames", "someObject", "bar");

To publish information to both of these topics, you pass dojo.publish the topic names and arrays of the arguments that you want to pass to subscribed functions, as follows

/* 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.publish("globalEvents", ["data from an interesting source"]);
dojo.publish("fullNames", ["Alex", "Russell"]);

To disconnect someObject.bar from its topic, you use dojo.disconnect, as follows:

/* 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.unsubscribe(topics[1]);