The most powerful and underutilized JavaScript utility is one and the same: the Dojo Toolkit. While nearly every JavaScript framework or toolkit promises to do everything you need, the Dojo Toolkit makes the most compelling case for that statement being true. This post will cover many of the most powerful features of the Dojo Toolkit, and in doing so will make the case for why you should use the Dojo Toolkit for your next project.
1. Modularity and AMD Loading
Don't start you next project without checking out all of the features Dojo has to offer!
As our client side JavaScript code grows in size, and it will, modularity will be key to keeping our applications fast, maintainable, and performant. The days of using one lump library file without asynchronous loading are over. For years, the Dojo Toolkit's code has been the shining example of modularity, using dojo.require
(before builds) to dynamically pull in only the resources required by the page. The default method of loading JavaScript resources was synchronously, though there was a cross-domain option which was asynchronous.
Dojo has since moved to an asynchronous loader, written by Rawld Gill, which masterfully loads all resources asynchronously, vastly improving speed. To load a few JavaScript resources, you can code something like the following:
// The require function directs the loader to attempt to load resources in the first array // If the resources have already been loaded, their cached objects will be used require( // An array of modules to load ["dojo/on", "dojo/touch", "dijit/form/Button", "dojo/domReady!"], // A callback function with loaded module objects as arguments // Must be added in the same order as they were loaded function(on, touch, Button) { // Now do something with the components we've loaded! });
To declare a module, simply code the following pattern:
// Using 'define' instead of 'require' because we're defining a module define( // Again, an array of module dependencies for the module we'd like to build ["dojo/aspect", "dojo/_base/declare", "dijit/layout/BorderContainer"] // Again, a callback function which should return an object function(aspect, declare, BorderContainer) { // Return a module (object, function, or Dojo declared class) return declare("mynamespace.layout.CustomBorderContainer", [BorderContainer], { // Custom attributes and methods here }); })
This simple define
method, used by nearly all AMD loaders, is incredibly simple and structured. very much like a require block, so it's very easy to use. The items listed in the dependency array are loaded before the callback is run. The callback (usually) returns a function or object representing the module. An easy pattern that loads fast, maintains modularity, and allows developers to load only what they need!
Dojo's feature-rich loader also provides plugins, such as domReady, for listening for DOM readiness, and has feature detection with hasJS. The loader is also intelligent enough to conditionally load modules based on environment or configuration:
// This code is featured in the dojo/Deferred module define([ "./has", "./_base/lang", "./errors/CancelError", "./promise/Promise", "./has!config-deferredInstrumentation?./promise/instrumentation" ], function(has, lang, CancelError, Promise, instrumentation){ // ... });
Not only is Dojo modular as can be, it provides a baked-in loader for you!
Dojo Module and AMD Resources
2. Classes and Extensibility with dojo/declare
While JavaScript doesn't provide a true class system, the Dojo Toolkit provides a class-like inheritance pattern using dojo/declare
. Declare is used throughout the framework so that developers can:
- cut down on or even eliminate repeated code
- use "mixins" to share functionality amongst many other classes
- easily extend existing classes for increased customization
- share modules of code between different projects
- safely create "fixed" classes when there's a bug in an existing Dojo class
Dojo's class system uses prototypal inheritance, allowing prototypes to be inherited and thus children classes can be as powerful as parents due to the shared prototype. Using dojo/declare
is incredibly easy:
// Of course we need to use define to create the module define([ // Load dojo/declare dependency "dojo/declare", // Also load dependencies of the class we intend to create "dijit/form/Button", "dojo/on", "mynamespace/_MyButtonMixin" // Mixins start with "_" ], function(declare, Button, on, _MyButtonMixin) { // Return a declare() product, i.e. a class return declare( // First argument is the widget name, if you're creating one // Must be in object syntax format "mynamespace.CustomButton", // The second argument is a single object whose prototype will be used as a base for the new class // An array can also be used, for multiple inheritance [ Button, _MyButtonMixin ], // Lastly, an object which contains new properties and methods, or // different values for inherited properties and methods { myCustomProperty: true, value: "Hello!", myCustomMethod: function() { // Do stuff here! }, methodThatOverridesParent: function(val) { this.myCustomMethod(val); // Calling "this.inherited(arguments)" runs the parent's method // of the same, passing the same params return this.inherited(arguments); } } ); });
While the class above doesn't set out to accomplish a real task (it's simply an example), it illustrates code reuse, via the inheritance chain and mixins; it also shows how a child class can call a parent class' same method to cut down on repeated code.
Another advantage to using Dojo's class system is that all properties and methods are customizable -- there is no "options" object that limits the amount of properties customizable on Dojo classes. Everything is easily changed and extended throughout the class creation process.
3. Aspects and "Function to Function Events"
Aspects are one of the most powerful and essential pieces of advanced web application development...and the Dojo Toolkit has provided them for years. Instead of triggering functionality after a traditional user event, like click
, mouseover
, or keyup
, aspects allow you to trigger function B before or after function A is executed. Essentially, you can connect functions to functions -- brilliant!
Triggering a function after another function looks like:
// after(target, methodName, advisingFunction, receiveArguments); aspect.after(myObject, "someMethod", function(arg1, arg2) { // Execute functionality after the myObject.doSomething function fires }, true);
Ensuring that function B fires before function A is just as easy!
aspect.before(myObject, "someMethod", function(arg1, arg2) { // This function fires *before* the original myObject.someMethod does });
Aspects are extremely helpful when creating advanced UI's with Dijit. Listening for events on one widget or class can trigger a change in other widgets, allowing developers to create one large, controlling widget out of many small:
var self = this; aspect.after(this.submitButton, "onClick", function() { // The submit button was clicked, trigger more functionality self.showAjaxSpinner(); });
The aspect resource was previously found with dojo.connect.
Aspect Resources
4. Deferreds and Unified AJAX Transports
I cannot endorse this UI framework enough. When I say it's unparalleled, I cannot emphasize how much I mean it. There is nothing close.
Deferreds are object-based representations of asynchronous operations, allowing for async operation states to easily be passed from one place to another. One of jQuery's most recent and important additions was Deferreds. Coincidentally, the mantra of the Dojo team is "Dojo did it." The Dojo Toolkit has featured Deferreds for several years, using them for simple and advanced AJAX operations, animations, and more.
Along with being on the forefront of Deferred objects, Dojo also pioneered several IO handling methods outside of standard XMLHTTPRequest, including a window.name
wrapper, dojo/io/iframe
for AJAX file uploading, and more. So when are Deferred objects used within Dojo? Whenever an asynchronous / AJAX action takes place! Deferreds are returned from XHR requests, dojo/io
requests, animations, and more!
// Fire an AJAX request, getting the Deferred in return var def = xhr.get({ url: "/getSomePage" }); // Do lots o' callbacks def.then(function(result) { result.prop = 'Something more'; return result; }).then(function(resultObjWithProp) { // .... }).then(function() { // .... });
And then what does dojo/io/iframe
's API look like?
require(["dojo/io/iframe"], function(ioIframe){ // Send the request ioIframe.send({ form: "myform", url: "handler.php", handleAs: "json" // Handle the success result }).then(function(data){ // Do something // Handle the error case }, function(err){ // Handle Error }). then(function() { // More callbacks! }) });
The beauty in Dojo using Deferreds for each AJAX operation is that, no matter the method, you always know you'll receive a Deferred in return, speeding up development and unifying the API. Dojo 1.8 will see the introduction of dojo/request
, a new consolidation of AJAX methods. Here are a few examples of how the dojo/request
API will be used in the future:
// The most basic of AJAX requests require(["dojo/request"], function(request){ request("request.html").then(function(response){ // do something with results }, function(err){ // handle an error condition }, function(evt){ // handle a progress event }); });
A unified API makes development faster and code more compact; the new dojo/request
module by Bryan Forbes promises to make Dojo even more developer friendly!
Deferred and AJAX Resources
5. Dijit UI Framework
Without a doubt, the Dojo Toolkit's biggest advantage over other JavaScript frameworks is its Dijit UI framework. This unparalleled set of layout, form, and other tools boasts:
- complete, "out of the box" localization
- full accessibility support
- advanced layout widgets to ease the pains of 100% height elements, effort in creating custom splitters and layout modification, etc.
- form widgets with increased usability and built in validation
- many themes, the newest of which is called "claro"
- LESS files for custom themeing
- very modular code, allowing for ultimate customization and extension of existing widgets
Dijit also allows for declarative and programmatic widget creation; declarative widget creation looks like:
<div data-dojo-type="dijit.form.Button" data-dojo-props="label:'Click Me!'"></div>
...whereby traditional JavaScript widget creation looks like:
require(["dijit/form/Button"], function(Button) { // Create the button programmatically var button = new Button({ label: 'Click Me!' }, "myNodeId"); });
There are several dozen Dijit widgets provided within the dijit namespace, and a few dozen more available within the dojox namespace. The Dijit UI framework isn't just a few helpful UI widgets, as something like jQueryUI is; Dijit is a enterprise-ready, enterprise-tested UI framework.
Dijit UI Resources
- The Famous Dijit Themetester
- Creating Template-based Widgets
- Layout with Dijit
- Dijit Themes, Buttons, and Textboxes
- Dijit Tutorials
In my two years at SitePen, I worked almost exclusively with Dijit and the intricacies of creating flexible, localizable, efficient widgets. I cannot endorse this UI framework enough. When I say it's unparalleled, I cannot emphasize how much I mean it. There is nothing close.
6. Dojo Mobile
As with almost every problem on the web, Dojo has a solution; in this case, Dojo's answer to mobile lives within the dojox/mobile namespace. Dojo's excellent mobile solution provides:
- a device detection utility
- themes for iOS, Android, Blackberry, and "common" theme
- mobile form widgets
- layout widgets and panes
- support for desktop, allowing for easier debugging
Mobile widgets can be created declaratively or programmatically, just like Dijit widgets. Mobile views can be lazily rendered and swapping between views is seamless. The HTML anatomy of a dojox/mobile page is fairly simple:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/> <meta name="apple-mobile-web-app-capable" content="yes" /> <title>Your Application Name</title> <!-- custom stylesheets will go here --> <!-- dojo/javascript will go here --> </head> <body> <!-- application will go here --> </body> </html>
By using dojox/mobile/deviceTheme
, we can detect the user device and apply the proper theme:
// Will apply the device theme base on UA detection require(["dojox/mobile/deviceTheme"]);
With the device theme in place, the next step is requiring the widgets used by our specific mobile application, as well as any other custom classes we desire:
// Pull in a few widgets require([ "dojox/mobile/ScrollableView", "dojox/mobile/Heading", "dojox/mobile/RoundRectList", "dojox/mobile/TabBar", "dojox/parser" ]);
Once the JavaScript resources have been required, it's time to declaratively add a series of views and widgets that make up the application:
<!-- sample taken from Dojo's TweetView: http://dojotoolkit.org/documentation/tutorials/1.7/mobile/tweetview/app/ --> <!-- tweets view --> <div id="tweets" data-dojo-type="dojox.mobile.ScrollableView" data-dojo-props="selected: true"> <h1 data-dojo-type="dojox.mobile.Heading"> <!-- the refresh button --> <div data-dojo-type="dojox.mobile.ToolBarButton" data-dojo-props="icon: 'images/refresh.png'" class="mblDomButton tweetviewRefresh" style="float:right;"></div> Tweets </h1> <ul data-dojo-type="dojox.mobile.RoundRectList"> <li data-dojo-type="dojox.mobile.ListItem"> Tweet item here </li> </ul> </div> <!-- mentions view --> <div id="mentions" data-dojo-type="dojox.mobile.ScrollableView"> <h1 data-dojo-type="dojox.mobile.Heading"> <!-- the refresh button --> <div data-dojo-type="dojox.mobile.ToolBarButton" data-dojo-props="icon: 'images/refresh.png'" class="mblDomButton tweetviewRefresh" style="float:right;"></div> Mentions </h1> <ul data-dojo-type="dojox.mobile.RoundRectList"> <li data-dojo-type="dojox.mobile.ListItem"> Mention tweet item here </li> </ul> </div> <!-- settings view --> <div id="settings" data-dojo-type="dojox.mobile.ScrollableView"> <h1 data-dojo-type="dojox.mobile.Heading">Settings</h1> <h2 data-dojo-type="dojox.mobile.RoundRectCategory">Show</h2> <ul data-dojo-type="dojox.mobile.RoundRectList"> <li data-dojo-type="dojox.mobile.ListItem"> Setting item here </li> </ul> </div>
One incredible advantage to using dojox/mobile is that the API for widget creation is the same as all other Dijit classes, so speed in development is increased for those that have used Dijit before; for those that are new to Dojo, the mobile API is still incredibly easy.
dojox/mobile Resources
7. GFX and Charting
Without a doubt, the Dojo Toolkit’s biggest advantage over other JavaScript frameworks is its Dijit UI framework.
CSS animations are a great visualization tool, as is animated imagery, but neither are as flexible and powerful as vector graphic creation and manipulation. The most popular client side vector graphic generation tool has always been Raphael JS, but Dojo's GFX library is unquestionably more powerful. GFX can be configured to render vector graphics in SVG, VML, Silverlight, Canvas, and WebGL. GFX provides a usable wrapper to create each vector graphic shape (ellipse, line, path, etc.) for speed in development, and allows developers to:
- Skew, rotate, and resize graphics
- Animate fill, stroker, and other graphic properties
- Add linear and circular gradients to a shape
- Listen and respond to mouse events
- Group shapes for easier management and animation
Creating a simple set of shapes over a canvas could look like:
require(["dojox/gfx", "dojo/domReady"], function(gfx) { gfx.renderer = "canvas"; // Create a GFX surface // Arguments: node, width, height surface = gfx.createSurface("surfaceElement", 400, 400); // Create a circle with a set "blue" color surface.createCircle({ cx: 50, cy: 50, rx: 50, r: 25 }).setFill("blue"); // Crate a circle with a set hex color surface.createCircle({ cx: 300, cy: 300, rx: 50, r: 25 }).setFill("#f00"); // Create a circle with a linear gradient surface.createRect({x: 180, y: 40, width: 200, height: 100 }). setFill({ type:"linear", x1: 0, y1: 0, //x: 0=>0, consistent gradient horizontally x2: 0, //y: 0=>420, changing gradient vertically y2: 420, colors: [ { offset: 0, color: "#003b80" }, { offset: 0.5, color: "#0072e5" }, { offset: 1, color: "#4ea1fc" } ] }); // Create a circle with a radial gradient surface.createEllipse({ cx: 120, cy: 260, rx: 100, ry: 100 }).setFill({ type: "radial", cx: 150, cy: 200, colors: [ { offset: 0, color: "#4ea1fc" }, { offset: 0.5, color: "#0072e5" }, { offset: 1, color: "#003b80" } ] }); });
An API that's been written on top of GFX is Dojo's powerful dojox/charting library. Visualization of data via charting is popular and for good reason; simply reading numbers doesn't provide, well, the full picture. The dojox/charting library allows for:
- multiple plots
- animated chart elements
- plugins, including MoveSlice (animates pie chart slices), Tooltip, Magnify, and Highlight
- self-updating charts, powered by Dojo data stores
A basic pie chart can be created using the following Dojo JavaScript code:
<script> // x and y coordinates used for easy understanding of where they should display // Data represents website visits over a week period chartData = [ { x: 1, y: 19021 }, { x: 1, y: 12837 }, { x: 1, y: 12378 }, { x: 1, y: 21882 }, { x: 1, y: 17654 }, { x: 1, y: 15833 }, { x: 1, y: 16122 } ]; require([ // Require the widget parser "dojo/parser", // Require the basic 2d chart resource "dojox/charting/widget/Chart", // Require the theme of our choosing "dojox/charting/themes/Claro", // Charting plugins: // Require the Pie type of Plot "dojox/charting/plot2d/Pie" ]); </script> <!-- create the chart --> <div data-dojo-type="dojox.charting.widget.Chart" data-dojo-props="theme:dojox.charting.themes.Claro" id="viewsChart" style="width: 550px; height: 550px;"> <!-- Pie Chart: add the plot --> <div class="plot" name="default" type="Pie" radius="200" fontColor="#000" labelOffset="-20"></div> <!-- pieData is the data source --> <div class="series" name="Last Week's Visits" array="chartData"></div> </div>
While the code above creates a simple pie chart, Dojo's dojox/charting library is capable of much, much more.
dojox/gfx and dojox/charting Resources
- Vector Graphics with Dojo's GFX
- Interactive AJAX London Logo
- Dojo Charting
- Advanced Charting with Dojo
- Dojo GFX Demos
8. SitePen's dgrid
SitePen, a JavaScript consultancy founded by Dojo Founder Dylan Schiemann, sought out to replace DojoX's clunky and bloated Grid widgets with a very fast, extensible, and editable grid widget; they've accomplished that task with dgrid. dgrid features:
- numerous themes and is easily themeable
- complete mobile compatability
- sortable rows
- onDemand grid utilities, allowing for lazy-loading of grid data
- tree-grid capabilities
- editable grid contents using Dijit widgets
- extensions including column resizing, drag and drop, pagination, and more
SitePen has done an outstanding job documenting each component of dgrid, so getting started creating your own feature-rich grids will be incredibly easy!
dgrid Resources
9. DOH Testing Framework
Not only is Dojo modular as can be, it provides a baked-in loader for you!
Testing is as important, if not more important, on the client side than server side. With the range of browsers available, and the varying number of features provided in each browser version, client side interactivity testing is a must. The Dojo Toolkit's own testing framework, nicknamed DOH (Dojo Objective Harness), is provided with each Dojo version download. Test writing is incredibly easy, and tests can be provided in a few different formats:
// Declare out the name of the test module to make dojo's module loader happy. dojo.provide("my.test.module"); // Register a test suite doh.register("MyTests", [ // Tests can be just a simple function... function assertTrueTest(){ doh.assertTrue(true); doh.assertTrue(1); doh.assertTrue(!false); }, // ... or an object with name, setUp, tearDown, and runTest properties { name: "thingerTest", setUp: function(){ this.thingerToTest = new Thinger(); this.thingerToTest.doStuffToInit(); }, runTest: function(){ doh.assertEqual("blah", this.thingerToTest.blahProp); doh.assertFalse(this.thingerToTest.falseProp); // ... }, tearDown: function(){ } }, // ... ]);
The test above is a very basic example of a Dojo test, but what about a more difficult situation, i.e. asynchronous actions? The most obvious asynchrnous action is an AJAX request, but animations and other Deferred-powered actions will create such a situation. DOH provides an incredibly easy method for testing asynchronous actions using doh.Deferred objects:
{ name: "Testing deferred interaction", timeout: 5000, runTest: function() { var deferred = new doh.Deferred(); myWidget.doAjaxAction().then(deferred.getTestCallback(function(){ doh.assertTrue(true); }); return deferred; } }
In the sample test above, the getTestCallback
function doesn't fire until doAjaxAction
is complete, and returns the success or failure of the test.
The subsequent tests don't move forward until the doh.Deferred resolves or times out, thus there are no test timing or overlap issues. DOH provides an incredibly reliable test suite that other client side frameworks simply do not provide. DOH also provides a Java-powered DOH robot which simulates real mouse and keyboard actions for more precise and realistic testing. If you hear Homer Simpson yell "Woohoo!", all of your tests pass; if you hear that dreaded "DOH!", your tests failed and you need to refactor your code.
DOH Resources
10. Dojo Build Process
When a web application is ready for release, it's incredibly important, for the sake of optimized load and cacheability, to create a minified, layered JavaScript file or file(s). This reduces requests and keeps site load as light as possible. Better yet is that Dojo's build system analyzes define
calls and uses them to automatically detect dependencies for builds. To use the Dojo build process, you create what's referred to as a build profile. Build profiles can contain numerous layers and may get quite complex, but the profile below is a simple example:
var profile = { releaseDir: "/path/to/releaseDir", basePath: "..", action: "release", cssOptimize: "comments", mini: true, optimize: "closure", layerOptimize: "closure", stripConsole: "all", selectorEngine: "acme", layers: { "dojo/dojo": { include: [ "dojo/dojo", "app/main" ], customBase: true, boot: true } }, resourceTags: { amd: function (filename, mid) { return /\.js$/.test(filename); } } };
Dojo's build process is extremely customizable, allowing the developer to customize:
- the minifier (Dojo's ShrinkSafe or Google Closure)
- the level of minification to be applied to the CSS files involved, if creating widgets
- where the build is output to
- the selector engine to be used within the build
- ...and much more!
Build profiles are run via the command line (recently rewritten for NodeJS), and the command line offers a variety of options to override or supplement settings within the build profile. A few examples of running the build profile include:
./build.sh --profile /path/to/app/app.profile.js --require /path/to/app/boot.js
The Dojo build process provides an incredible amount of control over the generated build files and rounds out the web application's optimization process. With the CSS and JS minified and layered to appropriate levels, your Dojo-powered app is ready for showtime!
11. BONUS! "Dojo's Treasure Chest": More DojoX
Two very prominent DojoX libraries have already been mentioned above, DojoX Mobile and GFX, but those are only two of the dozens of hidden treasures provide by Dojo. Those treasures include:
- extra layout and form widgets for Dijit
- advanced, localized form validation routines
- WebSocket and long-polling wrappers
- image widgets, including lightbox, slideshow, and gallery utilities
- advanced IO helpers
- advanced drag and drop libraries
- Nodelist extensions
These are only a few more of the dozens of gems within DojoX. Browse the Dojo checkout to find out more of the awesome fringe tools available!
The Dojo Toolkit is an all-encompassing JavaScript toolkit that provides:
- Basic JavaScript language and helper utilities
- Advanced Javascript language and AJAX utilties
- On-demand asynchronous script loading
- A complete UI framework
- A comprehensive testing suite
- Build tools
- ...and more!
Don't start you next project without checking out all of the features Dojo has to offer! Even if you don't need some of the advanced features listed above yet, using the Dojo Toolkit's most basic features (element querying, animations, XHR requests) will get you well on your was to creating a fast, feature-rich web application with no ceiling!
Comments