Cover | Table of Contents | Colophon
dojo.* function or attribute. Base comes packaged as a single file, dojo.js, which comes across the wire at under 30KB, and when you consider that most Flash-based advertisements that inundate the web are considerably larger than 30KB, such a small number seems quite amazing.
dojo.* function or attribute. Base comes packaged as a single file, dojo.js, which comes across the wire at under 30KB, and when you consider that most Flash-based advertisements that inundate the web are considerably larger than 30KB, such a small number seems quite amazing.SCRIPT tag that points to the file that loads some JavaScript code, and then magic elves inside of your web browser miraculously causes everything to "just work," right? Well, not quite. Like most other things in computing, it all comes back to pure and simple automation, and Dojo's bootstrap process is not different.<html>
<head>
<title>Title Goes Here</title>
<!-- A lightweight style sheet that smoothes out look and feel across browsers -->
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
<script
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
/* If needed, Dojo modules may be asynchronously requested into the page
here via dojo.require statements... */
dojo.addOnLoad(function( ) {
/* Any content that depends upon dojo.require statements goes here... */
});
</script>
</head>
<body>
<!-- ... -->
</body>
</html>dojo.byId, a toolkit-specific mechanism for looking up DOM nodes in a manner that is more portable and predictable than document.getElementById. Although dojo.byId absolutely pervades Dojo development, there is little value in repeating the previous discussion from ; refer back to the previous chapter for a detailed outline involving some of the issues with document.getElementById and how dojo.byId alleviates them. As a reminder, though, here's the full API call for dojo.byId :dojo.byId(/*String*/ id | /*DomNode*/ node, /*DomNode*/doc) // Returns a DOM Node
var foo = dojo.byId("foo"); //returns the node with id=foo if one exists
dojo.byId(foo).innerHTML="bar"; //the lookup is a no-op since foo is
//a node; then sets innerHTML to "bar"
var bar = dojo.byId("bar", baz); //returns the node with id=bar in document
//referenced by baz if one existsdojo.byId, a toolkit-specific mechanism for looking up DOM nodes in a manner that is more portable and predictable than document.getElementById. Although dojo.byId absolutely pervades Dojo development, there is little value in repeating the previous discussion from ; refer back to the previous chapter for a detailed outline involving some of the issues with document.getElementById and how dojo.byId alleviates them. As a reminder, though, here's the full API call for dojo.byId :dojo.byId(/*String*/ id | /*DomNode*/ node, /*DomNode*/doc) // Returns a DOM Node
var foo = dojo.byId("foo"); //returns the node with id=foo if one exists
dojo.byId(foo).innerHTML="bar"; //the lookup is a no-op since foo is
//a node; then sets innerHTML to "bar"
var bar = dojo.byId("bar", baz); //returns the node with id=bar in document
//referenced by baz if one existsisString(/*Any*/ value)true if value is a String.isArray(/*Any*/ value)true if value is an Array.isFunction(/*Any*/ value)true if value is a Function.isObject(/*Any*/ value)true if value is an Object (including an Array and Function ) or null.isArrayLike(/*Any*/ value)true if value is an Array but also allows for more permissive possibilities. For example, the built-in arguments value that can be accessed from within a Function object is especially an oddball in that it does not support built-in methods such as push ; however, it is array-like in that it is a list of values that can be indexed.isAlien(/*Any*/ value)true if value is a built-in function or native function such as an ActiveX component but does not respect the normal checks such as the instanceof Function.arguments member qualifying as an array via the isArrayLike function hopefully ties this theme together. When you consider the inherent dynamism in a language that does not require you to declare a particular variable to always be a particular data type (dynamic binding), duck typing is a great vehicle to inspect the type of an object when necessary.trim function instead of writing your own.trim at work:var s = " this is a value with whitespace padding each side "; s = dojo.trim(s); //"this is a value with whitespace padding each side"
dojo.string module via a dojo.require statement.dojo.string.paddojo.string.pad("", 5); // "00000"
dojo.string.pad("", 5, " "); // " "
dojo.string.pad("0", 5, "1"); // "11110"
dojo.string.pad("0", 5, "1", true); // "01111"dojo.string.substitute//Returns "Jack and Jill went up a hill."
dojo.string.substitute("${0} and ${1} went up a hill.", ["Jack", "Jill"]);
//"*Jack* and *Jill* went up a hill."
dojo.string.substitute("${person1} and ${person2} went up a hill.", {person1
: "Jack", person2: "Jill"});
dojo.string.substitute("${0} and ${1} went up a hill.", ["Jack", "Jill"],
function(x) {
return "*"+x+"*";
});dojo.string.trimstring module provides a slightly more efficient version of trim that can be used when performance really matters:dojo.string.trim( /* your string value */);
Array implementations wherever possible, but emulating functionality for browsers like IE when it is missing.Array object (http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference). As long as you have the toolkit with you, you'll never be caught defenseless again. And in case you have already forgotten from our discussion of dojo.byId in that you really can't take much for granted in the current browser ecosystem, the next section should reinvigorate your enthusiasm and might even surprise you.dojo.indexOf and dojo.lastIndexOf. Each of these functions returns an integer that provides the index of the element if it exists; the value -1 is returned if the element was not found at all. These function signatures also include an additional parameter that indicates the value that should be used as an initial location in case you don't want to start from the very beginning or end of the array. The signature is the same for each function:dojo.indexOf(/*Array*/ array, /*Any*/ value, /*Integer?*/ fromIndex) //returns Integer dojo.lastIndexOf(/*Array*/ array, /*Any*/ value, /*Integer?*/ fromIndex) //returns Integer
import statement or a #include preprocessor directive. Dojo's official means of accomplishing the same kind of concept is via dojo.provide and dojo.require, respectively.dojo.require and dojo.provide. In short, you include a dojo.provide statement as the first line of a file that you want to make available for a dojo.require statement to pull into a page. As it turns out, dojo.require is a lot more than just a placeholder like a SCRIPT tag; it takes care of mapping a module to a particular location on disk, fetching the code, and caching modules and resources that have previously been dojo.require d. Given that each dojo.require statement incurs at least one round trip call to the server if the resource is not already loaded, the caching can turn out to be a tremendous optimization; even the caching that you gain from requiring a resource one time and ensuring it is available locally from that point forward is a great optimization.mixin, extend, and clone.mixin and extend in its dojo.declare implementation, which is the toolkit's mechanism for simulating class-based inheritance. dojo.declare is covered extensively in .new operator. As you might expect, it can sometimes be quite convenient to add additional properties to an object, whether it be to make something dynamic happen on-the-fly or as part of a well crafted design that maximizes code reuse. Either way, mixin provides a compact way of handling the implementation details for you.mixin used extensively throughout the toolkit to reuse blocks of code.mixin functions entails providing an indeterminate number of objects, where the first objects gets the other objects mixed into it:dojo.mixin(/*Object*/ o, /*Object*/ o, ...) //Returns Object
mixin:function Man( ) {
this.x = 10;
}
function Myth( ) {
this.y = 20;
}
function Legend( ) {
this.z = 30;
}
var theMan = new Man;
var theMyth = new Myth;
var theLegend = new Legend;
function ManMythLegend( ) {}
var theManTheMythTheLegend = new ManMythLegend;
//mutate theManTheMythTheLegend by mixing in the three objects
dojo.mixin(theManTheMythTheLegend, theMan, theMyth, theLegend);mixin are actual object instances—not function declarations.extend function works just like mixin except that it adds all properties and methods of the mixins to a constructor function's prototype so that all future instances created with the constructor will automatically include these new properties and methods:dojo.extend(/*Function*/constructor, /*Object*/props, ... ) //Returns Function
function Man( ) {
this.x = 10;
}
function Myth( ) {
this.y = 20;
}
function Legend( ) {
this.z = 30;
}
var theMan = new Man;
var theMyth = new Myth;
var theLegend = new Legend;
function ManMythLegend( ) {}
var theManTheMythTheLegend = new ManMythLegend;
window object provides the outermost layer of context for a web application, there may be times when you need to swap out the default context for another one. For example, you may want to persist the exact state of a session when the user exits an application, or you might have a custom execution environment that's already been preconfigured for a particular circumstance. Instead of having code that manually iterates over sets of conditions to configure the environment each time, you might opt to use Base's window facilities to swap out the existing context for another one.dojo.global object and dojo.doc at will. Note that while dojo.doc is simply a reference to the window.document by default, it does provide a uniform mechanism for identifying the context's current document, which again can be quite useful for situations in which managed object contexts are involved. dojo.body() is a shortcut for obtaining the body of a document.body element is not explicitly defined for a strict XHTML document and some other documents you may encounter.dojo.doc //Returns Document dojo.body( ) //Returns DomNode dojo.setContext(/*Object*/globalObject, /*Document*/globalDocument)
dojo.global environment or a different dojo.doc than the one that currently exists:dojo.withGlobal(/*Object*/globalObject, /*Function*/callback, /*Object*/thisObject, /*Array*/callbackArgs) dojo.withDoc(/*Object*/documentObject, /*Function*/callback, /*Object*/thisObject, /*Array*/callbackArgs)
document or window is not a well-tested usage of the toolkit, so you may encounter support issues if going down that route. Standard usage normally entails loading Dojo into every document where you plan to use it. For lightweight operations, however, the context functions discussed in this section should work fine.appendChild, removeChild, and so on. Still, there are many utilities that could make DOM manipulation a lot simpler, and this section is all about how Base helps to make that happen.isDescendant, shown in , is self-descriptive. You provide it two arguments (id values or actual nodes), where the first argument is the node of interest and the second argument is a potential ancestor. If the node of interest is in fact a member of the potential ancestor's DOM tree, the function returns true.Name | Return type | Comment |
|---|---|---|
dojo.isDescendant(/*String | DomNode*/node, /* String | DomNode*/potentialAncestor) | Boolean | Returns a Boolean value indicating if a node has a particular ancestor or not and works in nested hierarchies as would be expected. |
dojo.setSelectable function. Here's the self-descriptive API:dojo.setSelectable(/*String | DomNode*/node, /*Boolean*/selectable)
dojo.style function provides a comprehensive means of getting or setting individual style values for a particular node. Simply provide the node and a style value in DOM-accessor format (e.g., borderWidthdojo.require them into the page before trying to use them.djConfig to register module paths and be aware of the various other options you can pass into this structure to configure the bootstrap processdojo.addOnLoad and dojo.addOnUnload functions and be aware of how dojo.addOnLoad can protect you from creating race conditions in your codedojo.provide and dojo.requiremap, filter, and forEach functionsmixin and extendhasClass, removeClass, addClass, and toggleClasscoords and marginBox to manipulate the placement of DOM nodesArray processing utilitiesObject and DOM eventsdojo.connect machinery that you'll read about in the following section often involves a mouse event on a particular DOM node. Whenever you use Dojo, you can rest assured that the following mouse and keyboard events are supported in accordance with the W3C standard:onclickonmousedownonmouseuponmouseoveronmouseoutonmousemoveonkeydownonkeyuponkeypressonmouseenter and onmouseleave events are also supported.dojo.fixEvent(/*DOMEvent*/ evt, /*DOMNode*/ sender) //Returns DOMEvent
DOMEvent is the standard convention that'll be used in the rest of the book to refer to the DOM event objects.dojo.connect machinery that you'll read about in the following section often involves a mouse event on a particular DOM node. Whenever you use Dojo, you can rest assured that the following mouse and keyboard events are supported in accordance with the W3C standard:onclickonmousedownonmouseuponmouseoveronmouseoutonmousemoveonkeydownonkeyuponkeypressonmouseenter and onmouseleave events are also supported.dojo.fixEvent(/*DOMEvent*/ evt, /*DOMNode*/ sender) //Returns DOMEvent
DOMEvent is the standard convention that'll be used in the rest of the book to refer to the DOM event objects.DOMEvent.Name | Type | Comment |
|---|---|---|
bubbles | Boolean | Indicates whether the event can bubble up the DOM tree. |
cancelable | Boolean | Indicates whether the event can have its default action prevented. |
currentTarget | DOMNode | The current node whose event listeners are being processed. (Useful for when an event bubbles.) |
target | DOMNode | The node that originally received the event. |
type | String | The type of the event, e.g., mouseover. |
ctrlKey | Boolean | Indicates if the Ctrl key was depressed when the event fired. |
dojo.connect and dojo.disconnect. In short, you use dojo.connect to chain together a series of events. Each call to dojo.connect returns a handle that you should keep and explicitly pass to dojo.disconnect whenever you are ready to dispose of the connection. Conveniently, all handles are disconnected automatically when the page unloads, but manual management of the handles may be necessary for preventing memory leaks in long-running applications that invoke a lot of connections that are used temporarily. (This is particularly the case on IE.) Coming up is the API that was introduced in .dojo.connect before the page is loaded is a very common mistake and can cause you to sink a lot of time into trying to debug something that isn't very easy to track down the first time you run into it. You should always set up your connections within the function that you pass into dojo.addOnLoad to stay safe./* Set up a connection */
dojo.connect(/*Object|null*/ obj,
/*String*/ event,
/*Object|null*/ context,
/*String|Function*/ method) // Returns a Handle
/* Tear down a connection */
dojo.disconnect(/*Handle*/handle);dojo.connect as an opaque object that you don't do anything with except pass to disconnect at a later time. (In case you're wondering, it is nothing special—just a collection of information that is used to manage the connection internally.)dojo.connect is exactly what you'll need to solve a problem, there are also a lot of times when you'll want a much more indirect "broadcast" style of communication in which various widgets communicate anonymously. For these circumstances, you might instead use dojo.publish and dojo.subscribe.dojo.connect connections for what seems like one cohesive action, it's considerably simpler to have one widget publish a notification that an event has transpired (optionally passing along data with it) and other widgets can subscribe to this notification and automatically take action accordingly. The beauty of the approach is that the object performing the broadcast doesn't need to know anything whatsoever about the other objects—or even if they exist, for that matter. Another classic example for this kind of communication involves portlets—pluggable interface components (http://en.wikipedia.org/wiki/Portlet) that are managed within a web portal, kind of like a dashboard.dojo.connect standardizes the event Object that is passed into event-handling functions, providing portability across platformsdojo.connect allows you to arbitrarily chain DOM events, JavaScript Object events, and ordinary functions together to create an event-driven responsedojo.connect versus pub/sub in an application architectureDeferred, a class that forms the lynchpin in the toolkit's IO subsystem by providing a uniform interface for handling asynchronous activity.XMLHttpRequest object allows them to now behave much like traditional desktop app