Chapter 11. Dijit Overview

Dijit is the fantastic layer of widgets that the toolkit provides as drop-in replacements for the standard HTML web controls. This chapter paves the way for Part II by starting out with a fairly nontechnical discussion of Dijit's motivation, philosophy, and goals as an off-the-shelf resource, which dovetails into a discussion of how designers and ordinary page creators can use dijits in markup with little to no programming. The chapter concludes with a guided tour that provides a thorough overview of everything included in Dijit.

Motivation for Dijit

Web development is very much an engineering problem, and it has quite an intriguing history, filled with clever hacking and ingenuity. Although the web browser may be the ultimate platform from a conceptual standpoint, the problem from an engineering perspective is that virtually every interesting use case for delivering a rich user experience requires workarounds and augmentation of some form or another. From a very conservative estimate, the lack of conformance to standards by major industry players has produced a landscape littered with more than a half-dozen viable configurations, and along the increase of powerful mobile devices with web browsing capabilities, that number is only going to continue growing.

Consequently, developing maintainable applications for the Web has become more difficult than ever; if you don't support as many configurations as possible, you lose market share, popularity, and revenue. Coupling support for various configurations with the already unwieldy yet fairly common practices of mixing HTML, CSS, and JavaScript in fairly ad-hoc ways makes the effort a seemingly impossible effort.

You already know that Base and Core insulate you from browser inconsistencies and minimize the time you spend writing workarounds; Dijit leverages all of the goods from Base and Core to provide an extensible framework for building modular, reusable user interface widgets.

Although technically incorrect, many Dojo users think of "Dijit" as synonymous with "Dojo" because its widgets are in high demand. Still, Dijit is its own subproject in the toolkit and its logical segmentation from the rest of the toolkit makes it easier to manage and improve as time goes by. In addition to providing you with a collection of off-the-shelf widgets, Dijit provides the infrastructure for you to build your own widgets—the same infrastructure that Dijit uses.

Some specific goals of Dijit include:

  • Developing a standard set of common widgets for web development in an analogous manner to the way that Swing provides an interface for Java applications or Cocoa provides interface controls for an OS X application

  • Leveraging existing machinery from Core and Base to keep the implementation of widgets as simple and portable as possible

  • Conforming to accessibility (a11y) standards in accordance with the ARIA (Accessibility for Rich Internet Applications) specification to support the visually impaired and users who cannot use a mouse

  • Requiring that all widgets be globalized, which simplifies internationalization initiatives by ensuring that widgets are localized and supporting cultural formats and bidirectional (bidi) content

  • Maintaining a coherent API so that developers can transfer knowledge across multiple widgets and reuse patterns for solving problems

  • Supporting a consistent look and feel with stylesheets, yet making widgets easily customizable

  • Ensuring that the creation of widgets in markup is just as easy as with JavaScript (or easier)

  • Making it simple to augment an existing page with a widget or to scale multiple widgets into a full-blown application

  • Providing full support for bidirectional text (realized as of version 1.1)

  • Supporting the most common browsers across multiple platforms, including Internet Explorer 6+, Firefox 2+, and Safari 3+[19]

Low Coupling, High Cohesion

Perhaps the most important advantage that Dijit brings to your web development efforts is the ability encapsulate user interface components into standalone widgets. If you've done web development for any amount of time, you've no doubt run into the problem of trying to wrap up the HTML, CSS, and JavaScript source files for a user interface into a portable package that is capable of instantiating itself and delivering the intended functionality at the right time with minimal intervention.

In programming lingo, the problem of developing modular user interface components is well explained by the terms cohesion and coupling. Cohesion is a measure of how well the source code and resources work together to deliver a piece of functionality, while coupling is a measure of a module's dependency on other modules. When designing things like widgets, your goal is almost always to maximize cohesion and minimize coupling.

Dijit answers the call admirably, and even better, it is a layer of infrastructure that you don't have to write, debug, and maintain. Building off of Base's dojo.declare function for simulating classes, as shown in Figure 11-1, Dijit throws in standard lifecycle methods for creation and destruction, a standardized means of responding to events such as key strokes and mouse movements, and the ability to wrap up the visible presentation. It also makes it possible to manage it all via markup or JavaScript—delivering the very same functionality to two distinct audiences.

Juxtaposing a dijit as a collection of physical resources on disk versus a dijit as a JavaScript Function object
Figure 11-1. Juxtaposing a dijit as a collection of physical resources on disk versus a dijit as a JavaScript Function object

As a designer, snapping a dijit into a page via markup is as simple as including a special dojoType tag that the parser recognizes and instantiates into an event-driven DHTML entity. For example, the following snippet, adapted from Chapter 1, illustrates how simple it is to include a customized text box for approximately validating an email address as part of a form—all in markup:

<input type="text"
    length=25
    name="email"
    dojoType="dijit.form.ValidationTextBox"
    trim="true"
    lowercase="true"
    regExp="[a-z0-9._%+-]+@[a-z0-9-]+\.[a-z]{2,4}"
    required="true"
    invalidMessage="Please enter a valid e-mail address"/>

That's it. Not a single line of JavaScript is required to actually use the widget. Sure, many developers may need to develop or extend widgets, which entails writing JavaScript, but the beauty is that once it's written, it becomes a part of your off-the-shelf arsenal. When the page loads, the parser finds the dojoType tag, requests any additional resources that are needed (if any) from back on the server, and transplants a DHTML widget into the page. Laying out a user interface should be that easy!

Of course, anything you can do in markup is also possible with JavaScript. You can dynamically create the very same widget just like any ordinary JavaScript object and insert it into the page with a trivial amount of effort.

As a general pattern, dijit constructor functions have the following signature that accepts a collection of configuration properties and a node reference:

dijit.WidgetName(/*Object*/props, /*DOMNode|String*/node)

Each dijit has a DOM node reference that is its visible representation, and inserting the DOM node reference into the page is all that is necessary to present it to the user. Once visible, its event handlers are exposed, and it behaves as though they were there all along. Here's how you would programmatically construct the same dijit for validating an email address; the parallel between the two approaches is apparent:

<script type="text/javascript">
    var w = new dijit.form.ValidationTextBox({
        length : 25,
        name : "email",
        trim : true,
        lowercase : true,
        regExp : "[a-z0-9._%+-]+@[a-z0-9-]+\.[a-z]{2,4}",
        required : true,
        invalidMessage : "Please enter a valid e-mail address"
    }, n); // n is a node reference somewhere in the page
</script>

Accessibility (a11y)

Accessibility is an increasingly important topic in the information age. In addition to a common goal of delivering content to the widest audience possible (with or without disability), political power such as Section 508[20] and other established legislation that sets a minimal standard for technology being accessible to persons with disabilities, and there are economic incentives as well: the U.S. Department of Labor estimates that the discretionary spending of people with disabilities is in the neighborhood of 175 billion dollars (http://www.usdoj.gov/crt/ada/busstat.htm). No matter how you look at it and what your motives might be, a11y is an issue that's hard to ignore.

Common a11y Issues

While this short section cannot even begin to address the myriad details associated with successfully implementing a web application, it should raise your awareness of the issues involved and highlight the ways that Dijit addresses them. Two of the most common accessibility tasks involve supporting users with impaired vision who need screen readers and users who require the ability to completely navigate an application using only the keyboard.

By default, Dijit inherently supports both audiences. Accessibility for users with impaired vision is addressed by detecting if the browser is operating in high-contrast mode and by detecting whether images are disabled for Internet Explorer or Firefox.[21] If either accessibility-enabling condition is detected, dijits are rendered according to augmented style, images, and templates as necessary.

For example, Figure 11-2 illustrates the rendering for a dijit.ProgressBar in both standard and high-contrast mode.

Top: the automatic rendering of dijit.ProgressBar when accessibility conditions are detected; bottom: the standard dijit.ProgressBar
Figure 11-2. Top: the automatic rendering of dijit.ProgressBar when accessibility conditions are detected; bottom: the standard dijit.ProgressBar

Although some of the implementation details can be tedious, here's a basic rule of thumb that goes a long way to achieving accessible widgets for the blind: don't use images (CSS background images or standard images placed with the IMG tag) in such a way that the functionality of a page is impaired if they go missing. A corollary that follows is to ensure alt descriptions are provided; it may seem dirt simple, and it's not always pretty, but it can often get the job done.

The stock widgets provide full keyboard support via standardized use of the tabIndex attribute for controlling the movement of the focus across the application. Additional machinery explicitly manages the focus of complex controls so that tool tips can be displayed as needed—which might always be the case if a screen reader is in use.[22]

WAI-ARIA

Accessibility initiatives for users with impaired vision and keyboard access are increasing, but in the modern era of Rich Internet Applications, additional support is needed. Common examples of additional support include ensuring users remain aware of changes in state from an XHR call that did not explicitly reload a page, and adequately handling Back button functionality for select actions.

The W3C Web Accessibility Initiative for Accessible Rich Internet Applications (WAI-ARIA) is an effort to ensure that AJAX-powered applications that mimic desktop-like functionality have a set of guidelines for delivering functionality to impaired users. Back in the early 1990s, screen readers could pretty much just read good old HTML. Nowadays, however, widgets are hacked out by lots of nested DIV elements and manipulated with AJAX, which has no meaning to a screen reader. Thus,WAI-ARIA provides the semantics needed to effectively convey information to the blind. For example, these semantics may inform the screen reader that a particular collection of nested DIV elements is a tree, a particular node in the tree currently has the focus, and pressing the Tab key switches focus to the "next" element.

Dijit exposes a collection of functions inspired by WAI-ARIA that are specifically tailored to facilitate adding accessibility to widgets. The W3C working draft "Roadmap for Accessible Rich Internet Applications" (http://www.w3.org/TR/aria-roadmap/) is a great starting point to start learning about ARIA and the overall Web Accessibility Initiative. Specific coverage of roles is outlined in "Roles for Accessible Rich Internet Applications" (http://www.w3.org/TR/aria-role/), while states are covered in "States and Properties Module for Accessible Rich Internet Applications" (http://www.w3.org/TR/aria-state/).

Table 11-1 summarizes the WAI functions.

Table 11-1. WAI functions

Function

Comment

onload( )

Automatically called to detect if the page is in high-contrast mode or has disabled images. You will normally not call this method directly because it is automatically called when the page loads.

hasWaiRole(/* DOMNode */ node)

Returns true if the node has a role attribute.

getWaiRole(/* DOMNode */ node)

Returns the role attribute for a node.

setWaiRole(/* DOMNode */ node, /* String */ role)

Sets a role attribute for a node.

removeWaiRole(/* DOMNode */ node)

Removes the role attribute from an element.

hasWaiState(/* DOMNode */ node,

/* String */ state)

Returns true if a node has a particular state.

getWaiState(/* DOMNode */ node,

/* String */ state)

Returns the state value attribute for a node.

setWaiState(/* DOMNode */ node,

/* String */ state,

* String */ value)

Sets a state value for a node.

removeWaiState(/* DOMNode */ node,

/* String */ state)

Removes a state from an element.

In terms of WAI-ARIA, role describes the purpose of a control, and examples of role values might be link, checkbox, toolbar, or slider. state describes the status of a control and is not necessarily a binary function. For example, a control with checkbox role may have a "checked" state that is set to mixed for a partial selection. Other examples of state include checked and disabled, which are both binary (true/false) values.

Dijit for Designers

The fundamentals for using an existing dijit in markup are quite simple: a dojoType tag specifies the type of dijit that should be placed in the page, attributes pass data into the widget upon creation, and extension points allow you to override existing widget behavior. While the dojoType tag is required, attributes usually are set to reasonable default values, and extension points always fall back to a reasonable implementation.

The difference between "methods" and "extension points" is purely a semantic one: methods are operations that the application programmer call directly to control a dijit. Extension points are methods that the application programmer does not call directly; the dijits calls them itself when an appropriate condition arises. For example, a widget might have an explicit method like setValue that could be called to programmatically adjust it, while a method like onKeyUp would be an extension point in that it gets called automatically each time a key is pressed.

There are several attributes, listed in Table 11-2, that are especially important to be aware of for out-of-the-box usage because these attributes are set directly on a widget's DOM Node. These attributes ultimately ensure that the dijit's underlying HTML markup is as customizable and "proper" as possible.

Table 11-2. Common dijit attributes

Attribute

Type

Comment

id

String

A unique identifier for the widget. By default, this value is automatically generated and guaranteed to be unique. If an explicit value is provided that is known already to be in use, the value is ignored, and a unique value is generated.

lang

String

The language to use for displaying the widget. The browser's locale settings are used by default. If an additional locale is desired, specify it via djConfig.extraLocale so the bundle will be available. (In general, this attribute is not used unless it's necessary to display multiple languages on a single page.)

dir

String

Bidirectional support as defined by the HTML DIR attribute. By default, this value is set to ltr (left to right) unless rtl is provided. No other values are valid.

style

String

HTML style attributes that should be passed into the widget's outermost DOM node. By default, no additional style attributes are passed.

title

String

The standard HTML title attribute that can be used for displaying tooltips when hovering over a dijit's DOM node.

class

String

CSS class information to apply to the outermost DOM node. This attribute is particularly useful for overriding all or part of a default theme.

Themes

A Dijit theme is a fully consistent collection of CSS rules that span across the entire set of widgets. To say that another way, you might think of Dijit as being skinnable, where a theme is a kind skin that you apply. If you need to pull some widgets off the shelf and put together something quickly, themes are especially good because CSS becomes one less thing that you have to worry about implementing. As of version 1.1, Dojo includes three great-looking, prepackaged themes:

Tundra

Predominantly light grey and light blue hues named after the Arctic landscape.

Soria

Predominantly blue on blue, both of which are fairly light hues. Controls have a noticeably glossy "Web 2.0 sheen" look to them. The inspiration for this theme was the beautiful blue sky from a set of photos from Soria, Spain.

Nihilo

Predominantly white with soft grey outlines with some greyish blue text. Some controls use a yellow shade to denote color. It is rumored that the inspiration for this theme is inspired from the ex nihilo concept (to create something out of nothing), with the goal of minimalist elegance—you barely notice it's there.

In your toolkit checkout, you can find a theme tester at dijit/themes/themeTester.html that demos the various dijits with a theme of your choice. Actually looking at the themes on your own screen is the best way to feel them out, as a black and white page can't really do them justice.

The structure of the themes directory has the following pattern, although each primary CSS file generally includes @import statements that pull in various other CSS files as part of a maintainable design (the build tools consolidate CSS files, so official builds only deliver the final file, which minimizes HTTP latency in fetching resources):

themes/
    tundra/
        tundra.css
        images/
            lots of static images
    soria/
        soria.css
        images/
            lots of static images
    nihilo/
        nihilo.css
            images/
                lots of static images
    <your custom theme could go here; just follow the pattern...>

Example 11-1 explicitly emphasizes the parts of the page that are pertinent to the theme.

Example 11-1. Using a theme
<html>
    <head>
        <title>Fun With the Themes!</title>

        <!-- pull in the tundra theme -->
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
        ></script>

        <script type="text/javascript">
            //require your dijits here
        </script>
    <head>

    <body class="tundra">
        <!-- use your dijits here -->
    </body>
</html>

You'll notice that actually using a theme is as simple as pulling in a stylesheet and applying the appropriate class name to the BODY tag, although you could have applied the class name to a different tag on the page if you had a good reason not to style the entire page with a single theme. Themes are designed to be applied to any arbitrary page element, whether it is the entire page, a specific DIV section, or a particular widget. This listing also shows that the dojo.css file is also retrieved, which is generally assumed to be the case because it contains some baseline style.

Switching out the theme would be as simple as replacing all of the tundra references with either soria or nihilo. That's it. And that's also as easy as it should be to reskin the page.

We won't belabor the topic of themes because it's really just a system of well-engineered CSS rules, and while it absolutely and positively makes all of the difference to the credibility and desirability of Dijit, it just isn't a topic that lends itself well to the current discussion. If you're interested in themes, however, be sure to pick up a good reference on CSS and start reading through the various CSS files. You'll see definitions like .tundra .dojoButton { /* style goes here */ }, which are usually self-descriptive and easy to locate in Dijit template files or in the page if you are inspecting with Firebug.

Nodes Versus Dijits, DOM Events Versus Dijit Methods

Important distinctions must be made between a dijit versus a DOM node: a dijit is a JavaScript Function object that is instantiated from a collection of resources that may include HTML markup, CSS, JavaScript, and static resources like images; the dijit's visible manifestation is inserted into the page by assigning its domNode attribute (the outermost node in its template) into the page.

The distinction between a dijit and DOM node can be further highlighted by juxtaposing the dojo.byId function, which returns a DOM node given a string value, and Dijit's own dijit.byId, which returns the dijit that is associated with a particular DOM node. The differences are listed in Table 11-3. Using Firebug to execute the two commands on the following Button accentuates the differences:

<button id="foo" dojoType="dijit.form.Button">click me</button>
Table 11-3. Difference between dojo.byId and dijit.byId

Command

Firebug console result

dojo.byId("foo")

<button
    id="foo"
    class="dijitStretch
    dijitButtonNode
    dijitButtonContents"
    waistate="labelledby-foo_label"
    wairole="button"
    type="button"
    dojoattachpoint="focusNode,titleNode"
    role="wairole:button"
    labelledby="foo_label"
    tabindex="0"
    valuenow=""
    disabled="false">

dijit.byId("foo")

[Widget dijit.form.Button, foo] _connects=[4] _attaches=[0] id=foo

The dojo.byId command returns the DOM node that provides the visible manifestation of an instantiated dijit.form.Button, while the dijit.byId returns a JavaScript Function object that can be examined for all of the standard dijit machinery.

An incredibly common mistake is to try and run a method on the result of a dojo.byId command. Remember that DOM nodes do not have dijit-related methods.

The corollary to the distinction between a dijit and a DOM node is the analogous distinction between a Dijit event and a DOM event. While many dijits have an onClick event, this event is quite different from a DOM node's onclick event in spite of the obvious similarity in naming convention. Take a moment to load and run the following page in the Firebug console; the output highlights the crux of the matter:

<html>
    <head>
        <title>Fun with Button Clicking!</title>


        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            djConfig="parseOnLoad:true"
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.Button");
            dojo.addOnLoad(function(  ) {
                dojo.connect(dojo.byId("foo"), "onclick", function(evt) {
                    console.log("connect fired for DOM Node onclick");
                });

                dojo.connect(dijit.byId("foo"), "onclick", function(evt) {
                    console.log("connect fired for dijit onclick");    //never!
                });

                dojo.connect(dijit.byId("foo"), "onClick", function(evt) {
                    console.log("connect fired for dijit onClick");
                });
            });
        </script>
    </head>
    <body class="tundra">
        <button id="foo" dojoType="dijit.form.Button" onclick="foo">click me
            <script type="dojo/method" event="onClick" args="evt">
                console.log("Button fired onClick");
            </script>
        </button>
    </body>
</html>

To summarize, this page defines a simple method in markup for a simple Button, provides an implementation for its onClick method, and defines three connections: one for the DOM node's onclick event, and connections for the dijit's onclick and onClick events. However, dijits do not have an onclick event, so the example demonstrates that the common mistake of trying to connect to it is a pitfall that can produce bugs that are quite hard to track down and fix.

The Parser

The Dojo parser is a Core resource that is the standard means of instantiating a widget defined in markup and ensuring that its visible representation, linked via its domNode, gets inserted into the page. Once the domNode is assigned into the page, the browser renders it on the page. So, while a widget's DOM node is the vital part of the dijit that makes it visible, the totality of the dijit is considerably more. This section provides an introduction to the parser, as well as play-by-play coverage on exactly how it works.

Parsing a Widget When the Page Loads

Aside from seeing some references in the introductory material in Chapter 1 and some exposure in the drag-and-drop examples from Chapter 7, the parser hasn't been formally introduced because its most common use case is instantiating widgets in a page. Without further ado, here's an official example of the parser instantiating a widget from markup. Note the emphasized lines in Example 11-2, which highlight where the parser-related action is happening.

Example 11-2. Automatically parsing a widget
<html>
    <head>
        <title>Fun With the Parser!</title>

        <!-- pull in the standard CSS that styles the stock dijits -->

        <link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="parseOnLoad:true"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.Button");
        </script>
    </head>

    <body class="tundra">
      <button dojoType="dijit.form.Button"
 >Sign Up!</button>
    </body>
</html>

To summarize what's happening, there's just a simple page that includes an off-the-shelf button from Dijit that does absolutely nothing except look pretty—but for the purposes of introducing the parser without diving into Dijit specifics, this is just fine. The only thing you need to know about the Button dijit at this time is that it is fetched via a call to dojo.require and inserted into the page via the dojoType tag.

Any custom addOnLoad logic you could include is executed after the widgets are parsed—making it safe for you to reference them.

You won't see any direct invocations of the parser in Example 11-2; that's by design. The vast majority of the time, you simply dojo.require dijits into your page, set the parseOnLoad flag in djConfig, and let the rest happen behind the scenes. In fact, that's all that occurs in this example. It's worth taking a moment to ponder just how absolutely elegant it is that you can take a dijit off the shelf and, in just a few keystrokes, insert it into the page. No additional headache, hassle, or fuss is required.

Manually Parsing a Widget

There are bound to be times when you will need to manually parse a page or some node in the DOM. Fortunately, it's just one function call away. Consider Example 11-3, a variation that manually parses the widget in the page.

Example 11-3. Manually parsing a page
<html>
    <head>
        <title>Hello Parser</title>

        <!-- pull in the standard CSS that styles the stock dijits -->

        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="parseOnLoad: false"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.Button");
            dojo.addOnLoad(function(  ) {
              dojo.parser.parse(  ); //manually parse after the page loads
            });
        </script>
    <head/>
    <body class="tundra" >
      <button dojoType="dijit.form.Button" >Sign Up!</button>
    </body>
</html>

Although manually parsing the entire page is useful, you'll more often need to manually parse a DOM node. The parser accepts an optional argument that provides the root of a node in the DOM tree that it scans for dojoType tags and instantiates. Thus, you provide the parent of the node you wish to parse to the parse function. Here's one possible modification of the previous code block that illustrates:

<script type="text/javascript">
  dojo.require("dojo.parser");
  dojo.require("dijit.form.Button");
  dojo.addOnLoad(function(  ) {
    //The parser traverses the DOM tree passed in and instantiates widgets.
    //In this case, the button is the only leaf in the tree, so it is all that
    //gets parsed
    dojo.parser.parse(document.getElementsByTagName("button")[0].parentNode);
  });
</script>

Trying to manually parse a widget on the page by passing the widget's DOM node into the parse method will fail, and you may not receive any visible indication that parsing failed. Fortunately, if you can locate a reference to a node, referencing its parent through the parentNode is just a few keystrokes away.

Demystifying the Parser

Although what the parser accomplishes really does seem like magic, it really just boils down to rigorous, well-designed automation. As you now know, the parser has two primary use cases: parsing the page on load via djConfig="parseOnLoad:true" or manually parsing a widget. This section elaborates on the details that go into making those two things happen.

Parsing a widget when the page loads entails three basic requirements:

  • Include parseOnLoad:true as a key/value pair to djConfig, which the parser will detect when it is loaded and use to trigger automatic parsing.

  • Require the parser via dojo.require("dojo.parser") so that the parser is available and can register an automatic call to dojo.parser.parse( ) when the page loads. Because no arguments are passed to the call, the entire body of the page provides the basis for parsing.

  • Provide dojoType tags as needed in the markup for widgets that should be parsed.

Manually parsing a widget that has already been defined in markup after the page loads is similar:

  • Require the parser via dojo.require("dojo.parser"). Because parseOnLoad is not detected to be true, no automatic call to dojo.parser.parse( ) occurs.

  • Provide the corresponding dojoType tag in the markup for a widget—maybe even dynamically after the page has already loaded.

  • Manually call dojo.parser.parse( ), optionally providing a specific DOM node as an argument as the starting point for the parsing operation.

But what about the actual parsing process? You know—the part about finding all of the dojoType tags and instantiating them into widgets? Again, it's all simple automation when you get right down to it. Here's exactly what happens:

  • dojo.query("[dojoType]") is called to deterministically fetch the nodes in the page that need to be parsed.

  • Class information (as in dojo.declare type classes) is distilled from each node; attributes are iterated over and lightweight type conversion is performed. Recall that attributes may provide information to a class's constructor.

  • Any dojo/method or dojo/connect script tags internal to the node are detected and scheduled for processing. (More on these in the upcoming section "“Defining Methods in Markup”.")

  • An instance of the class is created by using its constructor unless a markupFactory method is defined, in which case it is used. markupFactory is a special method that allows you to define a custom constructor function for widgets that need different initialization in markup than they do via programmatic creation. All dijits inherit from a base class, _Widget, which fires off a standard series of lifecycle methods. One of these lifecycle methods inserts the dijit's domNode into the page, which makes it visible. Lifecycle methods are discussed in detail in the next chapter.

  • If a jsId attribute is present, then the class instance is mapped to the global JavaScript namespace. (Common for data stores and widgets that you have a reason to make global.)

  • Any connections provided via dojo/connect or dojo/method SCRIPT tags in markup are processed (more on this later in the chapter) and each widget's startup lifecycle method is called. startup is another standard lifecycle method inherited from _Widget (coming up in the next chapter) which allows you to manipulate any widgets that are contained in the one being instantiated.

Hopefully, that didn't make you feel the same way that you did when you learned that Santa Claus wasn't real, but you had to learn sometime. The next chapter focuses exclusively on dijit lifecycle methods where dedicated coverage of these concepts is provided.

Hands-on Dijit with NumberSpinner

This section provides some hands-on usage for a pretty intuitive dijit—the dijit.form.NumberSpinner —to warm you up for the chapters that follow. First, we'll work through creating the dijit in markup, and then we'll follow up with programmatic creation.

Creating from Markup

As you learned from an earlier section on the parser, it's pretty trivial to stick a dijit into the page. You require in the resources, provide the dojoType attribute in a tag, and have the parser go to work. For completeness, Example 11-4 shows how we'd follow that very same pattern to instantiate a NumberSpinner dijit.

Example 11-4. Creating the NumberSpinner widget in markup
<html>
    <head>
        <title>Number Spinner Fun!</title>


        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />

        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
            djConfig="parseOnLoad: true"
        ></script>

        <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.NumberSpinner");
        </script>
    <head>
    <body class="tundra">
        <form> <!-- some really awesome form -->
            <input dojoType="dijit.form.NumberSpinner"
                constraints="{min:0,max:10000}" value=1000>
            </input>
            </form>
    </body>
</html>

Programmatic Creation

While you'll often create dijits in markup, programmatic creation is no less common and the process is the very same as creating any other Function Object because that's exactly what a dijit is—a Function Object. In general, the constructor for a dijit accepts two parameters. The first is an Object that provides properties that should be passed in, and these are the same properties that you would be including in the tag if you were creating in markup. The second parameter is a source node or the id for a source node that identifies the placeholder that the dijit should replace:

var d = new module.DijitName(/*Object*/props, /*DOMNode|String*/node)

Example 11-5 programmatically creates the NumberSpinner and produces the very same effect as Example 11-4.

Example 11-5. Programmatically creating the NumberSpinner widget
<html>
    <head>
        <title>Number Spinner Fun!</title>

        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
        <link rel="stylesheet" type="text/css"
          href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />
        <script
            type="text/javascript"
            src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js"
        ></script>

        <script type="text/javascript">
            dojo.require("dijit.form.NumberSpinner");
            dojo.addOnLoad(function(  ) {
                var ns = new dijit.form.NumberSpinner(
                    //props
                    {
                        constraints : {min:0,max:10000},
                        value : 1000
                    },
                    "foo" //node id
                );
                // do other stuff with ns here...

            });
        </script>
    <head>
    <body class="tundra">
        <form>
            <input id="foo"></input>
        </form>
    </body>
</html>

Lots of Niceties

This particular example displays a nice little text box with a control that allows you to adjust the value from either the arrow keys on the keyboard, by clicking the arrow controls on the dijit itself, or though manual keyboard entry. In any case, the min and max and key/value pairs are part of the constraints attribute that can be customized to form the upper and lower values for the spinner with the arrow keys or when clicking on the controls; the value attribute provides the default value just like in ordinary HTML. Manually changing the value via manual keyboard entry, however, still changes the value, which may trigger a tooltip-style error message. Figure 11-3 illustrates what happens.

Recall that Dojo attempts to supplement the existing fabric for developing web applications—not override it. Thus, common attributes for form elements such as the value attribute in the previous code listing's input element still work just like normal.

Left: a NumberSpinner dijit changing its value via keyboard or mouse control; right: the default display when manual keyboard entry enters a value that is out of bounds
Figure 11-3. Left: a NumberSpinner dijit changing its value via keyboard or mouse control; right: the default display when manual keyboard entry enters a value that is out of bounds

While techniques from djConfig showed key/value pairs expressing objects constructed in markup without the surrounding curly brackets like djConfig="parseOnLoad:true,isDebug:true", it is more the exception than the rule. Dijit requires that Object attributes in markup be expressed using braces like constraints="{min:0, max:100}".

You've probably already made the connection about the NumberSpinner 's keyboard entry and a11y, but there are some other niceties that are worth trying out right away. You've no doubt noticed the numeric formatting that is automatically applied to separate every three digits of numbers greater than 999. Note that if you were rendering the page for a supported locale that used a different separator for the digits, it would have happened automatically: locales like en-us use commas to separate values, like 1,000, while Spain, the es-es locale, for example, uses dots to separate the digits, like 1.000. Figure 11-4 demonstrates. Try it for yourself by modifying your default locale in djConfig. For example, to set a default locale of Spain, you could do the following:

djConfig="locale:'es-es'"

Remember that any values in djConfig that are strings need to contain additional quotes around them. The syntax for declaring an associative array inline makes this easy to forget, and unfortunately, the error messages that can result from forgetting it are not always helpful to point you in the right direction. Any djConfig settings need to be loading prior to Base bootstrapping.

Dijits handle special formatting for the supported locales right out of the box with no additional configuration required; the top NumberSpinner was configured with en-us while the bottom NumberSpinner was configured with es-es; the dijit internally took care of the formatting details
Figure 11-4. Dijits handle special formatting for the supported locales right out of the box with no additional configuration required; the top NumberSpinner was configured with en-us while the bottom NumberSpinner was configured with es-es; the dijit internally took care of the formatting details

Another great out-of-the-box feature that Dijit supports is the notion of controls being typematic—that is, they respond in special ways to holding down a mouse button or keyboard key. If you try holding down the mouse button on one of the controls for the NumberSpinner, you should notice that it gradually increases for the first 10 or so numbers and then eventually speeds up and moves rapidly. Not surprisingly, keyboard navigation works the very same way. The Page Up and Page Down buttons also work and adjust the values by multiples of 10 by default.

Defining Methods in Markup

In addition to being able to programmatically write ordinary JavaScript to control and extend widgets, Dojo also provides the ability to define the JavaScript directly in markup by including a special type="dojo/method" attribute in SCRIPT tags. This can be very convenient for designers as well as anyone who needs to rapidly prototype a new idea. What's especially notable about defining methods in markup is that the keyword this refers to the containing widget, so you have instant access to the context you'd expect.

Consider the following update to the example code listing that defines methods in markup:

<!-- snip -->

    <script type="text/javascript">
            dojo.require("dojo.parser");
            dojo.require("dijit.form.NumberSpinner");
            dojo.require("dijit.form.Button");
     </script>
    </head>
    <body class="tundra">
        <form>
            <div dojoType="dijit.form.NumberSpinner" jsId="mySpinner
"

                constraints="{min:0,max:10000}" value=1000>
                <script type="dojo/method">
                    dojo.mixin(this, {
                        reset : function(  ) { this.setValue(1000); }
                    });
                </script>
            </div>
        </form>
        <button dojoType="dijit.form.Button" onClick="mySpinner.reset(  )">
reset</button>
    </body>
</html>

To sum up the effect of the changes, the jsId attribute gave the NumberSpinner a global variable name mySpinner, which was referenced in a Button's onClick method. The actual body of the reset method was established by the special script tag included inside of the dijit. The script tag providing anonymous dojo/method is executed after its constructor runs, so that any values passed in via attributes included in markup would be available.

Also, note that whereas the previous listing used an input element to create the spinner, the new listing uses a div tag. The reason why an input tag will not work with the updated listing is that it does not allow innerHTML. The tag had to be switched to a type that does allow innerHTML in order for it to work. If you're wondering why a div tag wasn't used all along, it really comes back to one primary issue: the ability to have a semantically correct page that works without any JavaScript involved. In other words, the previous form using an input element is a semantically correct input control even if JavaScript were disabled (for whatever reason), whereas the div -based spinner is not. Most of the time, this isn't an problem, but when designing a degradable page, it is very important to know your basic HTML and be aware of these issues.

The dojo/method and dojo/connect script tags do not work inside of marked up elements that do not allow innerHTML. This isn't a Dojo thing; it's in accordance with the HTML specification. Although not demonstrated with this example, SCRIPT tags containing a type="dojo/connect" attribute allow you to set up connections in markup using the same pattern.

While the additional reset button may make a great addition for a mouse-based control, note that pressing the Escape key on the keyboard would have reset the spinner to its original value without any additional work at all.

As an improvement that produces the very same effect but with less code, consider the following change to the dojo/method script tag:

<script type="dojo/method" event="reset">
    this.setValue(1000);
</script>

Instead of being executed automatically a single time after the constructor, which is the case for anonymous dojo/method script tags, this approach performs the work of actually creating the reset method and attaching it to the widget on your behalf. If there had been arguments involved, an additional args attribute could have been specified. For example, args="foo,bar,baz" would have allowed for passing in three named arguments to a method defined in markup.

Overview of Stock Dijits

Because Dojo's widget collection is incredibly extensive, it can be easy to get lost. This section presents a concise inventory of dijits so that you may be familiarized with what's available.

Form Dijits

The very naming convention for a category of "form dijits" implies that the dijits are designed to be used inside of a form. While this is certainly true, form dijits may also be used outside of forms or in a special dijit.form.Form dijit that provides some extra methods and extension points. Here's a very brief overview of what is included in each of those chapters. Recall that all dijits are a11y compatible and easily internationalized, where applicable.

Go to http://archive.dojotoolkit.org/nightly/ to view the Dijit test harnesses that contain all of these widgets. It's a great way to get a feel for just how much breadth and depth there really is.

Form

A special container for form widgets that provides handy methods and extension points for serializing to JSON, validating the entire form's contents, setting values for the form all at once, and event handling when the form is submitted.

Button variations

Drop-in replacements for ordinary buttons based on BUTTON elements as well as other button-like controls based on INPUT elements like checkboxes and radio elements. Additional button variations include menu-style buttons that have drop-down values (sort of like combo boxes) that are commonly shown in toolbars, and toggle buttons that commonly appear in toolbars such as bold and italic buttons.

ComboBox

A combination of the functionality provided by an ordinary SELECT combo box and a text field defined with an INPUT element, allowing users to choose from pre-filled selections or typing in their own values.

FilteringSelect

A drop-in replacement for an ordinary SELECT element. It may be populated dynamically, making it great for situations in which a very large number of selections may be possible.

NumberSpinner

Similar to a text box based on an INPUT element except that controls allow for making incremental adjustments to the value.

Slider

A draggable handle attached to a scale that may be laid out vertically or horizontally. This widget provides a more interactive way of adjusting a value and is commonly used in conjunction with a display that involves resizing two-dimensional objects in real time.

Textarea

A drop-in replacement for an ordinary TEXTAREA element, but resizes as necessary to fit its enclosed content so that valuable screen real estate isn't lost when the potential size of content may not be predictable or always annotated.

SimpleTextarea

A drop-in replacement for an ordinary TEXTAREA element with some additional machinery to interact with the Form dijit container and layout dijits.

MultiSelect

A drop-in replacement for an ordinary SELECT element that has the multiple=true attribute set. Like SimpleTextarea, it contains some extra machinery for interaction with the Form dijit.

TextBox variations

An entire family of feature-rich widgets based upon the INPUT element with a special emphasis for custom validation of values and formatting for common fields like date, time, currency, numbers, etc. An incredible amount of functionality is packed into this family of widgets.

Layout Dijits

Traditional techniques for complex layouts used to involve extensive CSS work. While CSS may not be rocket science, writing, maintaining, and testing it on multiple browsers requires nontrivial effort—especially if you're not a CSS guru. Layout dijits allow the layout to be constructed in markup—without resorting to nested tables—which seems to have made laying out a page a lot simpler. Layout dijits, in general, may be arbitrarily nested, which allows for extremely sophisticated designs at a fraction of the time involved with more traditional CSS-based techniques. Here's a synopsis of what Dijit provides:

ContentPane

The most basic building block for a layout and provides the actual wrapper for layout tile. Although they could be used as standalones, one or more ContentPane dijits generally exist as part of a container widget.

TabContainer

A means of providing a familiar, tabbed layout with the tabs appearing horizontally or vertically. Simple layouts with TabContainer s generally involve a TabContainer combined with a ContentPane, although arbitrary nesting is always a possibility. Content for tabs that are not initially displayed may be lazy loaded.

StackContainer

Provides a means of displaying multiple containers of information, but with only one container shown at a time. For example, multiple ContentPane s might contain individual slides for a presentation, and a StackContainer could be used to combine them all into a presentation. StackContainer s are also very handy for applications that have multiple "screens" that need to be displayed without the page ever reloading.

AccordionContainer

Displays one tile at a time, and when another tile is selected, the previously displayed tile folds up with a smooth animation. Content for tiles that are not initially displayed may be lazy-loaded.

BorderContainer

Provides a convenient way to easily achieve a typical "headline" style or "sidebar" style layout where there are multiple tiles on the screen and some of them span the entire height and width while others do not. Achieving a more complex layout "border-style" layout with up to five tiles on the screen (four tiles around the edges and a center tile that fills in the remainder) is trivial to achieve.

Application Dijits

Application dijits are the "other" category; they are all very common elements for any application that even begins to approach RIA functionality. Menus, toolbars, dialog overlays, and rich text editors are all part of the mix, and these dijits are so easy to use that you can't avoid wanting to:

Dijit API Drive-By

The functions listed in Table 11-4 are too commonly used not to especially call out. They're available as part of Dijit Base and get pulled in whenever you require Dijit resources into the page. You can also fetch them by issuing a dojo.require("dijit.dijit") statement, as they are included in the standard build profile, which you'll read more about in Chapter 16.

For comprehensive API documentation, visit Dojo's online documentation at http://api.dojotoolkit.org.

Table 11-4. Commonly used Dijit functions

Function/Member

Comment

dijit.registry( )

The registry contains a complete record of all dijits that are on the page and may be used to explicitly iterate through them, to check for the existence of a particular dijit, etc. For example, you could uniformly manipulate every dijit on a page via the dijit.registry.forEach function or you could query the page for a particular type of widget via dijit.registry.byClass (where "class" is in the OOP sense).

dijit.byNode(/* DOM Node */ node)

Given a node, returns the dijit that represents this node.

dijit.getEnclosingWidget(/* DOM Node */ node)

Given a node, returns the dijit whose DOM tree contains this node. This method is especially handy for situations in which you need a quick reference to a dijit via a DOM event. For example, you might use this method to easily find a dijit via the target property of the event object that is associated with a mouse click when a user clicks the mouse on some part of a dijit.

dijit.getViewport( )

Returns the dimensions and scroll position of the viewable area of a browser window—extremely useful for programmatically placing objects on the screen when the exact screen resolution or window size cannot be assumed. Often used in animations.

dijit.byId(/* String */ id)

Looks up a dijit on the page by the id value included in its original dojoType tag or passed in through programmatic creation. This function differs from dojo.byId in that dojo.byId returns a DOM node, whereas this function returns an actual dijit (a Function object).

While these aren't the only API methods you'll want to be aware of, they're some of the most common ones, and they will save you a lot of time if you can remember that they exist.

Summary

After reading this chapter, you should understand:

  • The basic philosophy behind the design of Dijit

  • The importance of low coupling and high cohesion in developing a complex application and the way that Dijit leverages these concepts to encapsulate functionality in dijits

  • The importance of accessibility (a11y) in an application, as well as W3C Web Accessibility Initiative for Accessible Rich Internet Applications, and the basic a11y approach taken by Dijit

  • That dijits can be implemented in markup such that they provide the same functionality as they would had they been created programmatically, and how to apply dojo/method and dojo/connect SCRIPT tags to a dijit in markup

  • The difference between a DOM node and a dijit; the difference between dojo.byId and dijit.byId ; the difference between DOM events and dijit events

  • The basic steps the parser takes to instantiate a dijit that is defined in markup

  • The basic architectural breakdown of Dijit into form dijits, layout dijits, and general purpose application dijits, as well as where to start looking for a particular kind of dijit

Next we're going to take a look at Dijit's anatomy and lifecycle.



[19] Dijit does not officially support exactly the same array of browsers as Base and Core. The pragmatism behind the decision is that there just isn't a wide enough user base to justify the additional coding, testing, and maintenance for additional browsers like Opera or Konqueror. However, just because "official" support does not exist doesn't mean that it's necessarily difficult to get Dijits working on these platforms—especially when you consider that Konqueror, Firefox, and WebKit (the core of Safari) are all open source projects.

[20] Section 508 refers to a statutory section in the United States' Rehabilitation Act of 1973, requiring federal agencies to make reasonable accommodations to Americans with disabilities.

[21] The detection of high-contract mode works quite well on Internet Explorer for Windows, but not so well on the Mac or other browsers. Unfortunately, not all platforms or browsers currently support a11y facets to the same extent (or at all), so your mileage may vary.

[22] A screen reader is an assistive device that audibly manages the focus and valid actions that can be performed on active controls.

[23] For some browsers, manipulating DOM nodes that are in another window isn't even possible because of security restrictions.