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, “Dijit and Util” 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.
Tip
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, “Juxtaposing a dijit as a collection of physical resources
on disk versus a dijit as a JavaScript Function object”, 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.
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, Toolkit Overview,
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, “Top: the automatic rendering of dijit.ProgressBar when
accessibility conditions are detected; bottom: the standard
dijit.ProgressBar” illustrates the
rendering for a dijit.ProgressBar
in both standard and high-contrast mode.
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, “WAI functions” summarizes the WAI functions.
Table 11.1. WAI functions
|
Function |
Comment |
|---|---|
|
|
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. |
|
|
Returns |
|
|
Returns the |
|
|
Sets a |
|
|
Removes the |
|
Returns |
|
Returns the |
|
Sets a |
|
Removes a |
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.
Tip
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, “Common dijit attributes”, 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 |
|---|---|---|
|
|
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. |
|
|
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
|
|
|
String |
Bidirectional support
as defined by the HTML |
|
|
String |
HTML style attributes that should be passed into the widget's outermost DOM node. By default, no additional style attributes are passed. |
|
|
String |
The standard HTML
|
|
|
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, “Using a theme” 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, “Difference between dojo.byId and dijit.byId”. 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 |
|---|---|
|
|
<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">
|
|
|
|
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.
Warning
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, Toolkit Overview and some exposure in the drag-and-drop examples from Chapter 7, Drag-and-Drop, 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, “Automatically parsing a widget”, 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.
Tip
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, “Automatically parsing a widget”; 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, “Manually parsing a page”, 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>Warning
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:trueas a key/value pair todjConfig, 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 todojo.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
dojoTypetags 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"). BecauseparseOnLoadis not detected to be true, no automatic call todojo.parser.parse( )occurs.Provide the corresponding
dojoTypetag 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.declaretype 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'sconstructor.Any
dojo/methodordojo/connectscript tags internal to the node are detected and scheduled for processing. (More on these in the upcoming section "the section called “Defining Methods in Markup”.")An instance of the class is created by using its
constructorunless amarkupFactorymethod is defined, in which case it is used.markupFactoryis 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'sdomNodeinto the page, which makes it visible. Lifecycle methods are discussed in detail in the next chapter.If a
jsIdattribute 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/connectordojo/method SCRIPTtags in markup are processed (more on this later in the chapter) and each widget'sstartuplifecycle method is called.startupis 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, “Creating the NumberSpinner widget in markup”
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 creating the NumberSpinner widget”
programmatically creates the NumberSpinner and produces the very same
effect as Example 11.4, “Creating the NumberSpinner widget in markup”.
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, “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” illustrates what
happens.
Tip
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.
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
Warning
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, “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” 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'"
Warning
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.
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.
Warning
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.
Tip
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.
ButtonvariationsDrop-in replacements for ordinary buttons based on
BUTTONelements as well as other button-like controls based onINPUTelements 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 asboldanditalicbuttons.-
ComboBox A combination of the functionality provided by an ordinary
SELECTcombo box and a text field defined with anINPUTelement, allowing users to choose from pre-filled selections or typing in their own values.-
FilteringSelect A drop-in replacement for an ordinary
SELECTelement. 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
INPUTelement 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
TEXTAREAelement, 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
TEXTAREAelement with some additional machinery to interact with theFormdijit container and layout dijits.-
MultiSelect A drop-in replacement for an ordinary
SELECTelement that has themultiple=trueattribute set. LikeSimpleTextarea, it contains some extra machinery for interaction with theFormdijit.TextBoxvariationsAn entire family of feature-rich widgets based upon the
INPUTelement 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
ContentPanedijits 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
TabContainers generally involve aTabContainercombined with aContentPane, 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
ContentPanes might contain individual slides for a presentation, and aStackContainercould be used to combine them all into a presentation.StackContainers 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:
-
Menu Provides a contextual popup menu similar to what is commonly seen from right-clicking in a desktop application.
Menuis also used to build complex buttons likeComboButtonandDropDownButtonto offer advanced functionality.-
Toolbar Provides a container for complex buttons such as
ToggleButtonthat supply the controls for a toolbar, although any button dijit may be included in a toolbar.-
Dialog Simulates an ordinary desktop dialog box, complete with a translucent overlay that prevents interaction with content "below" the dialog. Dialog dijits are a fantastic, easily maintainable alternative to pop-up windows for many use cases, especially when any kind of communication or DOM manipulation would be necessary between multiple windows.[23]
-
TooltipDialog A combination of
TooltipandDialog, which allows for delivering a dialog-style input in a tooltip. A key difference betweenDialogandTooltipDialogis that theTooltipDialogmay be dismissed by clicking anywhere not on theTooltipDialog, whereas aDialogprovides a translucent underlay that prevents interaction with the rest of the page until theDialogis explicitly closed.-
ProgressBar Models ordinary progress bars as commonly seen in virtually every desktop application.
ProgressBardijits are the standard way of providing feedback on a long-running operation or an asynchronous call back to the server that takes longer than a few seconds.ProgressBardijits may be determinate, providing a percentage complete as the indicator, or indeterminate, providing an arbitrary animation that indicates something is happening.-
TitlePane Offers functionality for displaying a pane of information with a title area on top. While the content of the pane may be closed or opened by clicking on an icon in the title area, the title area is always visible.
-
Tooltip A much more flexible alternative to the ordinary
titleattribute for ordinary HTML controls. Timing and arbitrary HTML may be included in the tooltip text.-
InlineEditBox A sort of widget wrapper that displays the widget's value, which appears like a label; however, the widget transforms into its editable form when the text is clicked. (Very rich functionality.)
-
ColorPalette By default, displays a 3 × 4 or 7 × 10 matrix of commonly used colors for users to select in a highly useful way.
ColorPalettemay be extended to display arbitrary color configurations.-
Editor Provides the equivalent of a minimally function rich-text editor, complete with a toolbar that is pre-configured for cut/copy/paste, undo/redo, text-alignment, basic markup such as bold/italic/strikethrough, and the ability to create bulleted lists. The toolbar may be customized as needed. A ridiculous amount of functionality is packed into this dijit, and it's much more lightweight than you might think, as
Editorbuilds off of specific native controls such as Firefox's Midas rich text editor.-
Tree Delivers a tree with nodes that may be arbitrarily nested and closed/expanded as needed. This interface control is commonly used to deliver long, hierarchical lists of information. Content for nodes that are not expanded by default may be lazy-loaded; this dijit uses the terrific
dojo.dataAPI to deliver its content.
Dijit API Drive-By
The functions listed in Table 11.4, “Commonly used Dijit functions” 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, Build Tools, Testing, and Production Considerations.
Tip
For comprehensive API documentation, visit Dojo's online documentation at http://api.dojotoolkit.org.
Table 11.4. Commonly used Dijit functions
|
Function/Member |
Comment |
|---|---|
|
|
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 |
|
|
Given a node, returns the dijit that represents this 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 |
|
|
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. |
|
|
Looks up a dijit on the
page by the |
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/methodanddojo/connectSCRIPT tags to a dijit in markupThe difference between a DOM node and a dijit; the difference between
dojo.byIdanddijit.byId; the difference between DOM events and dijit eventsThe 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.









Add a comment



Add a comment