Efficient Lazy Loading of a Tree
Dojo 1.4 sports a fantastic tree widget, complete with ARIA compliance, keyboard accessibility, and internationalization (including right-to-left layout for appropriate countries and languages). For large tree data sets, we want to be able to only load the necessary data for the visible nodes of the tree. As a user expands a node, we then want to load the children of that node. Ideally, we only want to make one HTTP request per expansion for optimal performance. Historically, effective lazy loading has been a challenge, but some recent additions will make it much easier to utilize efficient lazy loading mechanisms in the tree.
The Dojo tree widget supports lazy loading, but it typically connects to a Dojo data store for all its data through a model-store adapter. Consequently, the tree itself does not define the actual data loading mechanism, as that is up to the store. The tree merely requests data from the store as needed. However, the JsonRestStore fully supports lazy loading with a well defined mechanism for retrieving deferred data from the server. When the JsonRestStore is used with the tree widget, it makes it possible to lazy load nodes in the tree as nodes are expanded. JsonRestStore supports lazy loading by using JSON referencing, whereby items can be referenced from other properties and arrays and the full item can later be retrieved when needed (with loadItem).
The model-store adapter now in Dojo 1.4 and later supports an option for a loading mechanism that facilitates single requests per node expansion. Previously (in 1.3), when a node was expanded the tree would request all the children and if any of the children had not been loaded, the tree would request that each child’s data item be fully loaded (this is done to ensure that each child has a label and information about whether or not it has children so the child nodes can be properly rendered) resulting in a request for each child of the expanded node, which is clearly inefficient. However, now when the model’s deferItemLoadingUntilExpand property is set to true, the tree will not attempt to load all of the children, but rather will only load the item for the expanded node if needed. This allows us to leverage the JsonRestStore’s support for partially loaded items. When a node is expanded and the item is loaded, the server can provide a JSON representation of the item which includes references to the children, and a partial set of properties for each child, which can include the label and children information for proper rendering. Each of these children can then be fully loaded when they are expanded (with their partial children objects). With this strategy, each node expansion will rely on only a single HTTP request.
Let’s take a look at how to build a lazy loaded tree with the JsonRestStore. We begin by creating a store:
myStore = new dojox.data.JsonRestStore({target:"tree/", labelAttribute:"name"});Now, we will create the model adapter for the tree to access the store. This is where we use the new deferItemLoadingUntilExpand property:
myModel = new dijit.tree.ForestStoreModel({store: myStore,
deferItemLoadingUntilExpand: true,
query: "root",
childrenAttrs: ["children"]
});
Next, we can create a tree:
myTree = new dijit.Tree({model: myModel}, treeNode);myTree.startup();
Now, we can build our data that is supplied from the server. The first request that the JsonRestStore will make to the server will be a request for the top level nodes (the children of the root). The tree will make the initial request using the query provided to the model, and the JsonRestStore will combine that with the target. In this case, the request will be made to the path “tree/root”. Leveraging the partial loading support in JsonRestStore, we can serialize references to each of the items that will be children of the root node in the response, and only include the properties necessary to render these nodes (label and children info):
request:GET /tree/root
response:
[
{ $ref: ‘node1′, name:‘node1′, children:true},
{ $ref: ‘node2′, name:‘node2′},
]
This provides sufficient information to render the top level of the tree, as well as the link information for retrieving the full representation of each item. We don’t need to actually include the children, just the presence of a children property will indicate to the Tree that the node is expandable and an expansion icon will be included. Now when a user clicks on one of these nodes, the tree will ask the JsonRestStore to load the item and the JsonRestStore will request the resource for the URI specified by the $ref property. In this case, it will request node1. This URI is interpreted relative to the target URI of the store, so in this case, the JsonRestStore will request “tree/node1″. Your server can then respond:
request:GET /tree/node1
response:
{ id: ‘node1′, name:‘node1′, someProperty:’somePropertyA’, children:[
{ $ref: ‘node1.1′, name: ‘node1.1′, children: true},
{ $ref: ‘node1.2′, name: ‘node1.2′}
]}
Here we see the full representation of node1. This not only includes the name, but may include additional properties that are used in other application logic. We also include an array that lists all the children of this item. Once again we use partial representations of these children to minimize the data transferred over the wire to the client. Whenever any of these children are expanded the process will be repeated and another request will be made to the server for the full representation of that child.

The example code for lazy loaded trees is available in Dojo at /dijit/tests/Tree_with_JRS.html.
If you are updating data in the store, you should be aware of one more tip when using the Tree with the JsonRestStore. By default, the ForestStoreModel adapter will re-query the top nodes on every onNew notification event and every onDelete event that involves a top level item. This can result in queries to the server even though the server has not yet been sent all changes. This makes top level additions essentially disappear when the re-query takes place. You may need to override the _onNewItem and _onDeleteItem to provide your own logic about where new and deleted items should be placed in the hierarchy.
Another powerful feature of the referencing capabilities of JsonRestStore is that individual items can be referenced from multiple parent items. Consequently an item could exist in multiple places in the tree, under different parents.
Together, the Tree and the JsonRestStore provide a powerful combination for lazy loading data that allows for large extensive hierarchical data to be displayed without large upfront data transfers. The JsonRestStore’s partial loading support can be leveraged so that we can perform lazy loading with a single request per expansion.
Related posts:
Getting Started With Pintura
Pintura is a REST-style web framework that utilizes a layered approach to application development that facilitates straightforward, well-designed rich internet applications. Pintura forms the core web framework for Persevere 2.0, and consists of a powerful data modeling and persistence framework called Perstore, in combination with a REST-style web framework. You can read more about the goals and principles behind Pintura, but here we will look at how to get started writing applications.
Pintura-based applications normally consist of server-side data models with three layers: data stores, store models, and model facets. On top of this, different representation handlers (for serializing data to different formats) can be defined, but Pintura comes with a good set of these ( including JSON, JavaScript, multipart, and Atom), so usually that is not necessary. This provides a well-structured separation of concerns, distinguishing storage configuration (data stores), core data logic (models), varying capabilities of access to the data (facets), and data serialization (representations). Perhaps the easiest way to understand this approach to take a look at an example application.
Naturally we need to install Pintura first. Pintura is JavaScript based and can run on various CommonJS JavaScript servers including Node and Narwhal/Jack, and the various engines they can run on. The Pintura installation process is described here.
A basic Pintura application is very easy to create. If we start by making a copy of /template/, we can create an application with a server-side model class by simply adding this to the lib/app.js:
var Model = require("model").Model;Model("MyData", null, {
});
Once we start up our server for this application, we now have the full Persevere HTTP/REST interface for creating, querying, updating, and deleting objects from the MyTable model. We have everything we need on the server to write a web client application, which can utilize REST methods like GET, PUT, POST, and DELETE, (as well as JSON-RPC) to interact with the server model class to store and query data. We get an enormous amount of functionality for free, including cross-site capabilities (JSONP, window.name, and CORS support), cross-site request forgery protection, authentication, conditional request handling, and more.
However, most applications will require more server-side logic than simply a generic data store. Generally, application code will be written as model and facet definitions using Pintura’s persistence framework (Perstore). With this framework, we can define handlers for different aspects of the object’s interaction and lifecycle, such as get, put, and delete handlers. We can define data integrity contracts by adding JSON schema constraints in basically the same way as in Persevere 1.0 (except our model classes are defined with the Model constructor from the model module rather than a top-level Class constructor, to be more inline with CommonJS and standard web framework terminology). We can define additional methods for the model as well as methods for the persisted object instances (by defining methods on the model prototype), once again using the same structure as the first version of Persevere. For example, we could easily add a new method on object instances:
var Model = require("model").Model;Model("MyData", null, {
prototype: {
addFriend: function(friend){
this.friends.push(friend);
this.save();
}
}
});
Pintura comes with an example wiki, we will examine how it is constructed to see how we can further understand what it looks like to build an application with Pintura. The example wiki is found in the example folder in the Pintura distribution, and we will break out the various parts and how they fit together.
We will start by looking at the app.js file found in the example/lib directory. This is the entry module which loads the other modules, starting from a user perspective, first defining the application-specific resource representations and user access levels. The example/lib/access.js module defines a set of facets for accessing data, for the different sets of users. For each user, a set of facets is returned and the facet provides the capability through which the user can access the application data. We can then drill down into example/lib/facet/page.js module, which defines how the underlying model class can be accessed through each facet. The example/lib/model/page.js defines the core application logic which in turn accesses the data store that provides the low-level interaction with the underlying storage medium.
Let’s look more closely at each of the levels, starting at the bottom with the store. A data store is the basic entity which allows for RESTful interaction with a storage system. Storage systems can be relational SQL-based databases, document based databases, OS files, remote servers, and so on. The object store API follows the W3C Indexed Database API for an object store. Pintura’s Perstore framework comes with a variety of data store implementations, including a SQL datastore for Rhino, and a JavaScript/JSON file-based data store. Creating your own store is not difficult, it essentially involves implementing get(id), put(object, id), query(query), and delete(id) functions (and optionally create, transaction, and subscribe for additional functionality). In our example application, we don’t define any special stores, we will simply use the default store, which out of the box is the js-file module, an object store which saves records into a JavaScript/JSON file.
The object store API is not only used at the bottom storage level in Pintura, but the model class and facets also follow the same API for consistency; classes and facets are simply wrappers that expose the same store API, adding their own functionality and/or constraining operations. Model classes act as the gateway to stores, providing the central core logic of the application. Any application data logic that should be enforced for all users should be implemented here. Models do not need to implement the entire store API themselves. Any store method that is implemented simply passes through the underlying object store. An empty class is perfectly valid, and would result in all REST actions being passed through to the object store. In example/lib/model/page.js, we can see the put handler is implemented to add a lastModifiedBy property and create a new PageChange instance:
exports.Page = Model("Page", pageStore, {put: function(object, id){
object.lastModifiedBy = auth.currentUser.username;
…
We can also define the prototype for this class, which allows us to create methods that are available on all instances of the class.

At the next layer up, we have the set of facets used to access the Page class. Facets are defined in a similar way to model classes, they follow the store API, wrapping a class, and only methods with specific logic need to be implemented. There are two primary types of facets, restrictive and permissive facets. These differ in how they handle unimplemented methods. A permissive facet will default to passing all actions through to the underlying class. A restrictive facet defaults to only passing retrieval actions (get and query) through, methods have to be explicitly implemented to interact with the class.
In example/lib/facet/page.js we see three facets defined to accommodate different groups of users. The public facet is defined to be restrictive, essentially making the objects read-only for public users. The public facet also defines the query method so that only published pages are accessible. The user facet and admin facet are permissive, allowing all actions on the pages. All of these facets control access to the model class, and in fact, model classes are simply a special permissive facet that applies to data stores.
Continuing back up the stack, the access module can now be understood as the initial logic that determines which facets are available for the authenticated user, so their access is properly controlled.
The initial example/lib/app.js file also defines the custom media module used for generating the HTML for pages. Pintura comes with a good selection of media handlers out of the box; the JavaScript and JSON handlers are the primary use case handlers, allowing objects to easily be accessed through XHR by Ajax applications. Pintura is designed first and foremost for Ajax applications, so normally these will be used most frequently. However, we need a specific HTML handler in this case so that pages can be directly generated as HTML for easy indexing by search engines.
Media handlers are simple to implement. In the example/lib/media/wiki-html.js handler we can see that we just need to define the media type, the quality of the media type (for content negotiation), and the serializer. Bi-direction media handlers can also implement a deserialize method (for handling PUT and POST requests), but this isn’t needed for our Wiki, all updates to pages will be done through JSON-based XHR requests. The Wiki HTML handler simply uses the the wiky package to do the transformation of markup to HTML for us.
By default, Pintura does all the HTTP request handling for you, handling cross-site issues, authentication, transaction management, and converting requests to calls to facets and model classes. However, Pintura is highly modular, actually consisting of about 13 modular middleware appliances, many of which can be used on their own. With these set of modules, one can easily create new HTTP request handling stacks, or reuse individual modules for handling other requests. To see the Pintura stack, you can open up lib/pintura.js, and easily see the different middleware modules that come together in Pintura (this file also lists the set of media type handlers that Pintura registers by default).
For an application running on Jack, the Pintura application stack is defined as the request handler in jackconfig.js (in Node, it is defined in start-node.js). With these startup files we can define our own additional request handling logic, and still defer store related requests to the Pintura handler. This application definition defines static file handlers to simplify development. For the example application, we also created a very simple JSGI middleware module redirect-root that will check for requests to the root path, and redirect to a specific “root” wiki page. This middleware wraps the pintura.app request handler, handling the root path, and delegating all other requests to Pintura.
The example wiki gives us a good starting point for beginning to build with Pintura. Much of the persistence, model and facet APIs are defined at the Perstore project page. In the next article in this series, we will look at some more examples of how to leverage the persistence framework and its various stores.
Pintura provides the complete web client-server REST functionality of Persevere combined with a simple, modular, JavaScript-based, facet-oriented architecture, for easy to build to JavaScript applications, with end-to-end consistency, and flexible control and security.
Related posts:
Introducing Pintura
Pintura is a CommonJS/JSGI-compliant, server-side JavaScript-based framework for building rich Internet application with the REST architectural style, thin storage-oriented server design, and the consistency of end-to-end JavaScript. The Pintura framework forms the core of Persevere 2.0, and is the first framework built to run on multiple CommonJS platforms like node.js, Narwhal/Jack, and Flusspferd/Zest. It utilizes a layered approach to application development that facilitates straightforward, modular web applications.
Pintura is not a traditional MVC web server framework, which often conflate presentation and interaction concerns across the client and server, but rather follows the REST prescription of maintaining simple storage and serialization oriented server also known as thin server architecture or SOFEA. Pintura is designed to cleanly separate the concerns of presentation and interaction on the client, and storage and model logic concerns on the server. This design fits perfectly with comprehensive JavaScript frameworks like Dojo, General Interface, Cappuccino, YUI, and ExtJS that provide client-side MVC. In particular, Dojo has excellent support for standards-based JSON REST interaction that matches perfectly with this server-side framework.
The core design principles behind Pintura:
- Standards should be leveraged as much as possible to maximize interoperability with other projects, and facilitate freedom in composing web applications with the tools of choice. Pintura is based on RFC 2616 (HTTP/REST), JSON Schema, CommonJS, JSGI, JSON-RPC, and other interoperable standards.
- Presentation and data model logic should be properly separated such that clients can provide the user interface and servers can handle storage and application modeling logic. One should be able to develop multiple web clients for a given application data server.
- Storage and data processing concerns should be separated, with pluggable storage options that can easily be switched and combined (such MongoDB plus a MySQL database).
- The best option choice between configuration and convention is neither. Integration between different entities without dependence upon certain programming patterns (convention or configuration) is optimal.
- End-to-end consistency in data (JSON/JavaScript) handling provides the smoothest communication and interaction from client to server and back.
- The most common cases, like standard CRUD style applications, should be very easy, while still making it possible to build more unusual or sophisticated applications.
- A framework should be simple enough to read the source and easily extend or modify. Applications and frameworks should be organized into layers of functionality.
- Utilize object-capability design for security to allow for flexible security design while protecting against confused deputy style vulnerabilities.
- Full support throughout for promises, providing asynchronous functionality (used extensively by Node), while preserving clean encapsulated interfaces.
The server-side JavaScript community has dramatically changed since Persevere development started. There have been numerous exciting new server-side projects that have started like node.js, flusspferd, and Narwhal, utilizing the new generation of advanced high-performance JavaScript engines like V8, TraceMonkey, and JavaScriptCore. Persevere’s main focus is on enabling efficient client server interaction and for well-structured web application development, and matching this functionality with powerful new platforms is a great fit.
In many ways, Pintura represents a simplification of Persevere. With an entirely JavaScript-based modular design, it is very easy to dig in and understand how it works, and the complicated Java-JavaScript bridges have been eliminated. The central request handling mechanism is composed of about 13 middleware modules that can easily be rearranged and used on their own. From the ground up, Pintura has a simple, minimal, direct approach to providing REST functionality.
There are numerous other improvements over Persevere 1.0. Pintura introduces a new security system that is based on the object capability model and is therefore much more flexible and powerful than the previous purely role-based mechanism. Much scientific research has pointed to the superiority of capability based design and how it avoids dangerous confused deputy issues. Pintura utilizes “facets” (also known as proxies) to provide fine-grained access control to different stored objects. Each facet defines what actions are available for objects, with the flexibility of programmatic guard functions. In the end, security is essentially as simple as defining what facets are available for given users, and then defining what actions the facets allow.
Pintura also has moved to implementing the W3C proposed standard Indexed Database APIs object store interface for all data stores. Pintura is based around the Perstore persistence library, which implements this simple API consisting of a REST-style interface. The stores expose get, put, and delete methods and is used at multiple levels in the framework for simplicity and consistency, including for facets, model classes, as well as lower level object stores. Furthermore, additional functionality is easily applied to stores with provided store wrappers that add support for inheritance, caching, replication, event notification and more. Not only that, but with the interface likely to be implemented in browsers soon, this API will provide true end-to-end consistency.
Pintura has also embraced a much more asynchronous oriented design. JavaScript in the browser uses shared-nothing event loop concurrency, and server side projects like Node and Narwhal are following suit. This not only improves client-server consistency, but there is compelling evidence that suggests that event-based design performs and scales much better than traditional thread-based web servers. One of the major challenges of event-based systems is the encapsulation of functionality when dealing with callbacks, but promises are used extensively to greatly simplify working with asynchronous code.
Pintura also leverages the link relation definitions specified by the JSON schema specification that is now a submitted Internet-Draft. Link relations superset the functionality of JSON referencing, and allow for much cleaner, more flexible referencing syntax. This further advances the flexibility of a loosely coupled hyperlink-driven REST architecture.
A core feature of Pintura is now content negotiation, following the REST architectural design of permitting multiple representations of a resource. Resources effectively are persistable JavaScript objects, and representations allows these objects to be serialized and deserialized in multiple formats. Pintura has excellent RFC 2616 conformance to content negotiation and provides a number of out of the box media type handlers, such JSON, JavaScript, CSV, and more, while making it easy to create new custom media type handlers.
Finally, Pintura uses greatly simplified querying syntax. It continues to provide powerful URL-based querying, but the format is simplified to work easily with standard HTML form URL-encoding, aligns with the feed item query language, and has an easy to use and extensible call syntax. Pintura provides a parsing mechanism to help developers implement their own custom query handlers.
Role in Persevere 2.0Persevere 2.0 is not a single monolithic project, but will be a distribution of several web stack configurations composed of various components including Pintura, Perstore, and the store explorer. Pintura and Perstore are still alpha stage software, but the Persevere 2.0 system is shaping up quickly.

Pintura brings together the cumulative advances of modern JavaScript and web architecture design patterns, including object capability based security, event-driven flow, immutable promises, JSON Schema, REST style, and thin server architecture. Coupled with the powerful new CommonJS platforms and remarkably fast JavaScript engines, this framework fulfills a critical role in the next generation web application stack, and is built to adapt to your JS platform of choice. The next article in this series will explore how to get started with Pintura.
Related posts:
Managing Widget Templates in Dojo 1.4
This article introduces dojo.cache and presents a technique for externalizing your widget templates in swappable configuration files, where they can be referenced by a custom templateKey widget property.
Introducing dojo.cacheDojo 1.4 adds a new core utility called dojo.cache. To appreciate it we first have to review how Dijit’s templatePath works. When you define a _Templated widget with a templatePath property, the content from that URL is fetched the first time you instantiate the widget, and made available as a string. All subsequent instances get the cached string. Furthermore, when you run a build, your templatePaths get replaced with templateStrings and their content is inlined into the output from the build. This improves performance considerably by removing those synchronous XHR requests, while remaining transparent to the developer.
dojo.cache generalizes this pattern, making the same functionality available from Dojo Core—synchronous, cached content retrieval that gets inlined during the build. dojo.cache usage is much like dojo.moduleUrl:
dojo.require("dojo.cache");my.stringResource = dojo.cache("module.path", "relative.path.html" ); Runtime configuration
You’ll find many uses for dojo.cache. The ability to define and cache strings was immediately useful for me in the context of runtime configuration files. This is a fairly common technique in which you define and load modules ahead of most of the components to your app, to externalize constants and properties for the application that you don’t want to hard-code into each component. So, you might have something like this:
dojo.provide("myapp.defaultConfig");// dojo.require("myapp.defaultConfig") populates myapp.config
// with messages with a laid-back informal tone
myapp.config = {
loadingText: "We’re loading that for you, won’t be a sec.",
loadErrorText: "Ugh, that didn’t work out. Sorry."
};
…and a variation:
dojo.provide("myapp.formalConfig");// dojo.require("myapp.formalConfig") populates myapp.config
// with messages with a more formal tone
myapp.config = {
loadingText: "Fetching requested resource",
loadErrorText: "Error loading requested resource. Please accept our apologies."
};
In this example, switching the “tone” in your app is as simple as switching the one line that requires your config. Your code can use e.g. this.attr("content", myapp.config.loadingText) without having to know or care which variation it is supposed to be using.
Template configurationNow with dojo.cache, we can manage templates in the same way, using object paths which can be looked up and requested at runtime during development, and inlined by the build for production:
myapp.config = {templates: {
someWidget: dojo.cache("myapp.resources", "someWidget.html"),
otherWidget: dojo.cache("myapp.alternateResources", "otherWidget.html"),
collapsiblePanel: dojo.cache("dijit.templates", "TitlePane.html"),
}
}
The config lets us retrieve template strings by using a look-up like myapp.config.templates.someWidget. With Dojo, we can treat these types of object paths as strings and use dojo.getObject to receive the value they represent. In this sense the object path is like an address—it gives us the ability to reference a variable and potentially a long string like a template with a short, symbolic one. I use these configuration properties by referencing them in a (new and custom) templateKey widget property. The key provides a level of indirection that enables you to pull template management outside of your widget definition entirely. It looks like this:
<div dojoType="my.SomeWidget" templateKey="myapp.config.templates.someWidget"></div> Template ReuseWhen all that varies between a set of widget classes is the template markup, you can reuse one widget by simply passing the instance a new templateKey, for example:
<div dojoType="my.SomeWidget" templateKey="myapp.config.templates.someWidgetInversed"></div> The ImplementationTo make this happen we need to have all widgets use an extended dijit._Templated to put the templateKey property on the prototype, and handle it in the buildRendering step:
dojo.declare("myapp._Templated", dijit._Templated, {templateKey: "",
buildRendering: function() {
if(this.templateKey) {
this.templateString = dojo.getObject(this.templateKey);
}
this.inherited("buildRendering", arguments);
}
});
.. which gets used by all our custom templated widgets, e.g.
dojo.declare("myapp.SomeWidget", [dijit._Widget, myapp._Templated], {templateKey: "myapp.config.templates.someWidget", // an optional, default value
});
Now, when we run our build, the config file you’ve specified gets the inlined templates, and the templateKey look-up works just as before. We could even monkey-patch this functionality into dijit._Templated and wring yet more goodness from the out-of-the-box Dijit & DojoX widgets.
ConclusionDijit’s architecture has always let you override templates, but in practice this has typically necessitated a subclass. The combination of runtime configuration and dojo.cache gives you new, powerful options that keep specifics out of reusable components, while maintaining resources in a manageable way. This is just one new technique that falls out of Dojo 1.4. Would you use it? What have been the highlights of 1.4 for you?
Related posts:
CommonJS/JSGI: The Emerging JavaScript Application Server Platform
CommonJS (formerly known as ServerJS) has become the essential hub around the development of server side JavaScript (SSJS). SSJS for years has suffered from fragmentation, but the CommonJS project has provided the momentum to bring different frameworks together and start building interoperable modules. SSJS is ripe with potential; JavaScript has been soaring in popularity and the ECMAScript 5 specification was recently accepted. JavaScript has proven itself as the language of the web’s client-side (even ActionScript is a derivative of JavaScript). The opportunity to use the same language on both client and server is certainly most realistic and viable with JavaScript as it avoids the need for translation.
CommonJSCommonJS has focused on developing critical APIs for building reusable modules, particularly for server-side JavaScript environment. The server-side is generally based around database interaction, file I/O, HTTP serving, and the generation of data formats and HTML, whereas the client-side is based around DOM manipulation and the browser object model. There are certainly APIs that can be used on both sides, and JavaScript on the client and server invites the reuse of APIs where possible. The WebWorker, Indexed Database, and XHR APIs are promising to be enormously beneficial on the server side, and with excellent client server consistency. But still the server side requires special attention, and CommonJS is bringing the needed standards and conventions.
There have been a number of different areas that CommonJS has invested in for creating the necessary foundation for SSJS. The primary initial effort was in defining the module system, which provides an easy to use system for loading modules and their dependencies, while ensuring that each module is loaded only once. This module system is now very broadly implemented and extensively used by the other subsequent module APIs defined by CommonJS. The module system provides the essential basis for modular library and application development.
CommonJS is working on building up the low-level I/O components, defining the necessary binary data types, the APIs for file interaction, and encoding. There are proposals in place for unit testing, and a number of modules to provide concurrency support. And finally there is a HTTP interface specification known as JSGI.
ConcurrencyOne of the most central characteristics of a runtime is the concurrency approach. The inherent dangers of shared memory threading are widely understood, and the browsers wisely avoid exposing developers to the low level complications of shared-memory threads, alternately providing shared nothing event-loop concurrency. With the latter approach, data structures are not exposed to preemptive threads that could cause race conditions. Instead, events are queued up, and deterministically executed. Concurrent operations are handled with WebWorkers, in separate memory spaces, with the ability to communicate to other workers with message passing.
The CommonJS group defines APIs, so it is beyond the scope of CommonJS to dictate the concurrency model used by servers, but there is general consensus recommending that JavaScript-based servers utilize shared-nothing event loop concurrency, and CommonJS APIs are built to support this model. The CommonJS wiki includes proposals for exposing the WebWorker API through a worker module, and for exposing controls for the event loop with the event-queue module.
PromisesOne key abstraction that is extremely useful for event-based systems is a promise. A promise is an abstraction that encapsulates the execution of an asynchronous action and its subsequent value that it returns. A promise can be thought of as representing the result of an asynchronous computation. The promise will receive the value returned by the call when it is finished, and acts as a placeholder for the returned value. Promises are critical to developing large scale applications that make heavy use of asynchronicity. Using callback-passing for asynchronous actions does not compose well. It is impossible to properly encapsulate functionality, switching an implementation from synchronous to asynchronous breaks APIs and creates complex flows of passing callbacks around to handle return values. With promises, values can be returned just like synchronous functions, and callers can listen for asynchronous results without requiring additional callback-passing APIs.
The promise API in CommonJS is an asynchronous handling system based on the collective experience of the promises in Dojo, Twisted, and ref_send. The ref_send promises focus on secure immutable promises, but lacks a reasonable API for promise implementors. Dojo provides a promise API (called Deferreds) with convenient chaining support, but has suffered from the use of mutable promises, with side-effect inducing callbacks complicating the data flow. The promise API combines the best of both, providing secure immutable promises with a very simple, easy to implement API for implementors, and full chaining support. The promise API in CommonJS provides a foundation that implementors can easily extend to provide the convenience functions from Dojo’s Deferred API as well as ref_send’s static operators.
An example of using CommonJS promise might look like:
requestSomeData("http://example.com/foo") // returns a promise for the response.then(function(response){ // ‘then’ is used to provide a promise handler
return JSON.parse(response.body); // parse the body
}) // returns a promise for the parsed body
.then(function(data){
return data.price; // get the price
}) // returns a promise for the price
.then(function(price){ // print out the price when it is fulfilled
print("The price is " + price);
});
JSGI: HTTP Gateway
The JavaScript gate interface (JSGI) API has been asymptotically reaching consensus in the CommonJS group, as a standard API for serving HTTP requests through JavaScript application servers. The JSGI provides a very easy to use API for responding to HTTP requests, influenced by the design of WSGI and Rack. A basic JSGI application looks like:
app = function(request){return {
status: 200,
headers: {},
body: ["Hello World"]
};
};
JSGI has been intentionally designed to make it easy to write “middleware”, modules that exist at different places in the request handling chain to add various forms of functionality. Middleware can include support for cross-domain requests, logging, error handling, special method handling, mapping URLs to different applications, and much more.
Most existing HTTP interfaces were designed around a synchronous model, and have had an extremely difficult time adapting to the asynchronous model, which is essential for efficient Comet support. JSGI on the other hand, has been developed from the ground up with asynchronicity in mind, both to support Comet, and to work properly in an event-based environment. JSGI achieves support for asynchronicity while still maintaining a simple function call and return style by leveraging promises. Here we can see the power of promises, JSGI applications can return a promise to indicate that the response is not yet complete, and no additional callback parameter need to be passed around. With promise support throughout, an asynchronous JSGI app can be as simple as:
app = function(request){return doSomethingAsynchronous().then(function(result){
return {
status: 200,
headers: {},
body: ["Result ", result]
};
});
};
Since JSGI effectively provides a JSON/JavaScript representation of the HTTP protocol, JSGI has powerful opportunities for use in communication outside of just HTTP server handling. JSGI can be used as an API for making HTTP requests as a client (as a promise based alternative to XHR), and can be used as an inter-process/inter-worker protocol. HTTP is almost certainly the most commonly used communication protocol, and the RESTful power of HTTP can easily be leveraged for worker communication with JSGI’s JSON-based format (that doesn’t require HTTP parsing).
ProjectsCommonJS and JSGI are purely API specifications, not implementations. Lets look at some projects that implement these specifications.
Narwhal/JackProbably the most advanced and mature CommonJS implementation is Narwhal plus Jack. Basically all of the CommonJS APIs are implemented in Narwhal/Jack and/or their forks. Narwhal provides extensive I/O support, a large collection of useful library modules, and a great package management system called tusk with package resource handling built into its module loader. Narwhal is designed for running on various JavaScript platforms and has an intelligently architected layer of abstractions for dealing with different engines.
Narwhal has the most complete engine support for Rhino. Rhino is almost certainly the most popular JavaScript engine for server side JavaScript due to its seamless integration with Java. With Rhino, the virtually limitless expanse of Java libraries are available and easily accessible. While great efforts are being made to important libraries in other engines, the breadth of libraries that are available through the Rhino’s Java bridge is not likely to be matched anytime soon. It also comes with a good visual debugger. Rhino is also perhaps the most feature-rich JavaScript implementation available, as it implements most of the ECMAScript 5 specification and Mozilla’s JavaScript 1.8 features.
Narwhal also runs on JSCore, the JavaScript engine used by Safari. JSCore is a very high-performance engine (second only to V8, perhaps), and consequently there is great potential for high-performance applications running on Narwhal/JSCore.
Jack is a JSGI implementation that has been developed to run on Narwhal, and is also built to run on various web server technologies. Jack has been somewhat of the de facto reference implementation for JSGI, and runs on Simple, CGI, servlet containers (including Jetty and GAE), and others. Older versions of Jack relied on shared memory threading, but with the latest work on Jack, requests are handled with full event loop based shared-nothing concurrency, with request delegation across multiple workers for efficient multi-core utilization.
NodeNode is an exciting project in the SSJS realm, and has received a lot of publicity recently, for good reason. Node builds on the amazingly fast V8 engine and provides important low-level functionality. V8 is one of the fastest dynamic language engines on the planet. Node uses libev for an event-loop based asynchronous I/O model, providing the same type of event-loop shared-nothing concurrency used in Narwhal. However, Node has had a stronger emphasis on trying to provide asynchronous APIs for all I/O operations. By exposing almost all I/O operations as asynchronous events, Node rarely has to block while waiting for I/O, and is able to execute much more efficiently than spreading workloads across multiple blocking threads. The performance advantages to event-loops over threads are well-documented. This focus, in combination with the blazing fast V8 engine, promises to place Node as the performance king of dynamic language based application servers.
There are certainly gaps that need to be filled with Node. Node implements CommonJS’s module system, but does not implement other CommonJS APIs, and has been slow to add better support for interoperability. Node runs its HTTP server and handling on a single process/thread, which means it can’t utilize multi-cores yet (support for that is coming). Node does not support the JSGI specification; however, it is possible to run JSGI on Node with the JSGI-Node adapter that I built.
There are other important things like package support and module reloading that are definitely needed. From my experience, Node does not feel as complete as Narwhal. However, despite these concerns, Node has a lot of potential, and is an exciting project. Node primarily fulfills a role as a low-level JavaScript platform, on which higher level functionality can be built. The Node community is responsive. There is an effort to run Narwhal on top of Node, which would be a powerful combination.
EjscriptEjscript is a JavaScript platform that extends the JavaScript language with many new constructs. Many of these additions are based on the ECMAScript 4 specification that was eventually abandoned because many implementors felt it was too extensive of a revision of the language. However, many of the features included in ECMAScript 4 that Ejscript implements were technically excellent additions, and may be included in ECMAScript 6. Beyond extra language features, Ejscript boasts a very high performance engine with remarkably low memory footprints, claiming performance that rivals Node with less than a tenth of the memory consumption.
PerseverePersevere was one of the first projects to implement the CommonJS module specification. However, the primary goal of the Persevere project is to provide consistent end-to-end data semantics leveraging RESTful architecture. Persevere 1.0 provided CommonJS module support to provide interoperability with other CommonJS libraries, but being a JavaScript platform is beyond the primary scope of the project. With the new array of CommonJS platforms, future Persevere development will now focus on building on top of these new platforms and providing its data persistence, security, REST architecture, Comet notifications, and advanced web communication capabilities for multiple platforms.
OthersMozilla’s SpiderMonkey engine is also a capable engine for server side JavaScript. Flusspferd is a project to add low-level system functionality to SpiderMonkey and couples with Zest as a JSGI server and Juice as a web framework on top.
There are number of other important server-side projects including HelmaNG, with high-quality technology that predates CommonJS and now are adapting to work in harmony with the CommonJS ecosystem, but I won’t cover all of them.
Web FrameworksSome exciting new web frameworks are building on these technologies such as Nitro, Bogart, and JuiceJS. Also some platforms come with their own MVC framework such as HelmaNG, and EJScript. However, for many the move to JavaScript has been motivated by investment in rich client interfaces that already have presentation capabilities (the V and C in MVC), so a more modern REST-style, “thin server architecture” may be a better fit than traditional server-side MVC frameworks for many next generation JavaScript-based web applications. Pintura is the core of the next generation of Persevere, a cross-platform REST-style architecture web framework that is designed specifically for Ajax-style applications that maintain presentation logic on the client separate from the server-side storage concerns. We will cover Pintura in the next article of this series.
ConclusionThe combination of improvements and maturation of ECMAScript standards, with the new line of astonishingly fast JavaScript, the Ajax driven interest in JavaScript, the collaborative efforts of CommonJS standards, and the emerging array of server-side JavaScript projects are all coming together to propel server-side JavaScript to the forefront of web application development. It is not unreasonable to expect that the CommonJS APIs implemented with a web stack of the new line of SSJS projects could be the most important new web platform of the next decade.
Related posts:
Performance Testing of the Top 100 Sites is Misleading at Best
Recently, a number of performance tests have been released that are based on the performance of the top 100 web sites such as SpriteMe savings, the IE8 100 top sites test results, or the JSMeter research. These are in direct contrast with tests such as ACID3 which attempt to test the future of the web rather than just what’s possible today.
These efforts are outstanding and highly useful, especially the JSMeter work and their valiant effort to redefine performance tests that are indicative of today’s web apps.
I completely agree with one of their stated goals:
We hope our results will convince the JavaScript community to develop and adopt benchmarks that are more representative of real web applications.
However, I disagree with their approach: they are testing the performance of today’s already optimized sites! There’s nothing in the middle of testing today’s sites and the more unrealistic “test every feature” ACID tests.
I believe more accurate tests for tomorrow would be useful, testing what’s pushing the limits of the web today, but are not currently top 100 sites. My main objection with comparing performance across the top 100 web sites is this: The top 100 web sites are already relatively highly performant, because they are optimized for what’s possible today. They have and continue to improve thanks in large part to the work of Steve Souders and others in the performance optimization community. Because it costs significant amounts of money in server operations fees and bandwidth, high traffic web sites generally dedicate considerable resources to highly optimizing their sites. High traffic web sites also face significant competition and are highly scrutinized for acceptable page load time. Budget and competition result in popular sites not deploying code that makes pages load slower than their desired performance threshold. Even more importantly, top 100 sites have the budget to make their app work in the future when things change. You can dedicate people on your team to squeezing out performance improvements in all aspects if you have the budget for it. Most web apps cannot afford to do this.
When we’re testing the performance of new browsers or analyzing page load performance, we should also really be looking at what the top 100 sites will look like in terms of features and expectations in five years! So how do we do that today? There’s no simple answer, but here are some ideas:
- Test popular web apps, e.g. mint.com populated with large amounts of data
- Test apps that don’t support IE6, e.g. Google Wave
- Test all sections of popular sites, not just the home page, through an automated performance test harness
- Test ridiculous configurations of popular applications, e.g. enable every feature in modular applications until they slow down
- Test apps over long amounts of time in the browser, not just initial page load time
- Test 50 apps, each in a different tab, all at once, and see how fast you can make a browser like Firefox or IE crash!
- Test throttled networks that emulate the profile of mobile and satellite networks, slow hotel wi-fi networks that often limit the length and duration of connections, corporate proxies, tech conferences, and countries with overloaded pipes (e.g. YouTube in New Zealand)
Only when browsers are pushed to their limits do we see where they break down, and how sites break them. We also need tests and tools (such as instrumented usage of YSlow, PageSpeed, SpeedTracer, etc.) comparing the most complex apps and how they perform across the various browsers, as today’s complex app is potentially tomorrow’s median site.
To be clear, I’m not saying “don’t optimize for today”. I’m saying, stop comparing cutting edge sites to Google search results. Lumping these two together in a common test is like putting apples against oranges because they are both round fruit.
Related posts:
Dojo 1.4 Released!
Dojo 1.4 is hot off the presses, with more than seven months of significant improvements to performance, stability, and features.
Of particular interest:
- IO Pipeline topics
- dojo.cache
- dojo.contentHandlers
- dojo.hash with native HTML5 onhashchange event support where available
- Traversal and manipulation for NodeLists (the return value for dojo.query)
- dojo.ready (easier to type than dojo.addOnLoad)
- Hundreds of refinements to the Dijit API and collection of Dijits, and a few new widgets in DojoX
- DataChart widget and other improvements to charting
- dojox.drawing lands!
- Editor improvements and new plug-ins in both Dijit and DojoX
- Grid is faster, and the EnhancedGrid lands!
- ForestStoreModel for the TreeGrid
- GFX improvements
- dojox.jq, a very experimental module aimed at trying to match the jQuery API as close as possible, but using Dojo underneath
- Dojo build system optionally supports the Google Closure Tools compiler
- Significant speed improvements, especially in IE
Read the full Dojo 1.4 release notes for more details! And thanks to everyone in the Dojo community that helped make this release great!
Related posts:
Convergence of Chrome OS and Android?
Google recently was asked about something we have suspected: Android and Chrome OS may converge. From our perspective, Android and Chrome OS both offer compelling opportunities for building great web apps, but having two distinct operating systems from Google, each with different approaches to development, just adds complexity and confusion to the overall development landscape. Of course, it still bothers us that iPhone apps and Dashboard widgets aren’t interoperable.
Android has the first mover advantage of being deployed today to many devices, but to really get the most out of it, you really should develop using Java, or employ a toolkit like PhoneGap. Chrome OS offers the promise of using web technologies that are popular today, but is not yet production-ready, or optimized for mobile devices like Palm’s webOS.
The long-term expected convergence from Google makes sense. Convergence of Palm’s webOS with Chrome OS makes sense to us as well as they are both pushing towards having a very similar WebKit-based operating system for delivering web applications. Palm already appears to be moving in the direction of converging with Chrome through their adoption of key technologies in webOS such as Google’s V8 JavaScript engine. There’s nothing confirming that this will happen, other than it just making sense.
While there is a lot of short term interest in apps, you can still get significant results from mobile web apps, and the gap between a native app and a web-based app will quickly shrink, as evidenced by apps like Pie Guy. After all, web apps don’t require app store approval!
Related posts:
Gears is Dead? Long live Gears!
It was recently reported that Google Dumps Gears for HTML5. If true, with the investment Google has made in HTML5, Chrome, Chrome OS, and Chrome Frame, this is not surprising, but it does leave a potential short-term gap for offline application development.
In their post, Read-Write Web asks if offline access is even necessary any longer. I guess they don’t spend enough time on airplanes or at hotels and conferences with poor internet connectivity! It’s certainly necessary, and while other browsers have developed significant offline capabilities, older browsers still need a plug-in like Gears.
In the interim, what should you do if you want an offline application? Do you develop for Gears and HTML 5 features? Do you wait for Chrome Frame to integrate offline capabilities? Do you use a toolkit like Dojo which will wrap the various possibilities? Or do you rely on something else like AIR, Titanium, Prism, or Fluid? The answer really depends on your application. If it is live now, you plan for the future but you keep going with Gears and/or Dojo Offline. If you won’t be launching for some time, you may want to talk to us about your offline app options.
At the end of the day, Gears is open source. If there’s a long-term need for its existence, the community can pick it up and run with it! Thankfully end-of-life isn’t the certain death-knell that it is with closed-source software.
Related posts:
Persevere 1.0
Persevere 1.0 is now available for download. Persevere is a JavaScript storage and application server that uses a standards-based interface of HTTP/REST, JSON-RPC, JSONPath, and REST Channels. Persevere is designed for rich client applications and can be used with any framework or client. The Persevere Server runs on Rhino and provides persistent data storage of dynamic JSON data in an interactive server side JavaScript environment with the following key features:
- Create, read, update, and delete access to persistent data through a standard JSON HTTP/REST web interface
- Dynamic object persistence – expando objects, arrays, and JavaScript functions can be stored, for extensive JavaScript persistence support
- Remote execution of JavaScript methods on the server through JSON-RPC for a consistent client/server language platform
- Flexible and fast indexed query capability through JSONQuery/JSONPath
- Comet-based data monitoring capabilities through HTTP Channels with Bayeux transport plugin/negotiation support
- Data-centric role-based object level security with user management, Persevere is designed to be accessed securely through Ajax with public-facing sites
- Comprehensive referencing capabilities using JSON referencing, including circular, multiple, lazy, non-lazy, cross-data source, and cross-site referencing for a wide variety of object structures
- Data integrity and validation through JSON Schema based definitions
- Class-based data hierarchy – typed objects can have methods, inheritance, class-based querying
- Pluggable data source architectures – SQL tables, XML files, remote web services can be used as data stores
- Object versioning with transactional history of record states
Recently, Cramer Development put together a slick little application for making sticky notes. They discuss how quickly the application came together, as Persevere allowed them to quickly establish a data API, and then focus on the client side interface.
Other users include:
- DataStream Content Solutions is using Persevere to build an XML repository for legal data in combination with MarkLogic.
- Montana State University is using Persevere for their Yogo Data Management Project.
- Another multi-national company is using Persevere in production for Intranet applications, with consistent usage from a number of users.
- And, of course, we at SitePen are using Persevere for a number of the applications we are developing.
Numerous others are using Persevere in a variety of ways.
Learning moreThere are a number of resources for learning more about Persevere and getting started with it.
- Persevere Documentation
- Download Persevere
- SitePen Persevere Support
- SitePen Persevere Blog
- Getting Started with Persevere Using Dojo
- Persevere Mailing List
- Persevere + Dojo (using Comet) demonstration
With Persevere 1.0 finished, we are already working on the next version which will be based on the new Pintura architecture. Pintura is the new JavaScript core for the Persevere HTTP interface that is based on the CommonJS and JSGI API. Pintura will run on any CommonJS/JSGI capable JavaScript engine (support for V8, JSCore, and Spidermonkey coming).
Related posts:
Getting Help
In addition SitePen’s various commercial support options, there are a number of ways to find helpful information on your own. Dojo has a large community of developers and hobbyists all across the globe that are willing to assist with problems and offer guidance. Many tutorials and examples exist and are ready to be found, you just have to look.
Here are some vital community resources available to assist you in your Dojo-learning, and some hints to ensure success:
Dojo Search
Search first, ask later. A quick stop at the dojotoolkit.org search page usually turns up lots of commonly encountered problems. The new search engine has options to help you target specific resources in the Dojo community, like blogs, forums, or archived mailing lists.
Dojo Forums
If you are unable to find any discussion or book entry already, start a new topic in the Dojo forums.
It helps to provide examples contained within code tags, and to politely state your question. If you have tried other methods and failed, mention them as well. The more infomation you provide in your post, the more likely someone is going to quickly be able to assist you.
Also available on the Dojotoolkit website: a collection of Frequently Asked Questions.
#dojo
Join the #dojo chat room on the irc server irc.freenode.net. This room acts as a realtime development center for numbers of people ranging from beginner to expert. Often, many core Dojo developers are available for any level of discussion, at seemingly odd hours of the day. There is no experience requirement, just a desire to learn.
The conversations range from deeply technical to outlandishly silly. It is a very friendly room, and a great way to be in immediate contact with like minded people while developing or learning Dojo. The first rule in the channel topic “Don’t Ask to Ask, just Ask” means just that: Jump right in, and start talking. If help is available, you will likely get a response.
Mailing Lists
Though the forums have taken the place of the once-active mailing lists, this resource is still available, and the preference of some. Simply signup, and begin writing a thoughtful, well researched question, and you are typically going to receive a response. The more thought you put into your post, the more willing people will be to help you.
There are several thousand subscribers to dojo-interest, so civility is expected of everyone.
It is important to remember the Dojo community is entirely voluntary. People helping other people for the good of the Open Web, typically in their spare time. Civility is expected of everyone, and you are not guaranteed any speedy response, if at all. If you find things within the community to be lacking, you are always welcome to contribute. See the Getting Involved guide for more information about what you can do. The community grows daily, and your contributions are just as welcome as everybody elses.
Related posts:
Dijit: Prepackaged
Dojo’s widget system is called Dijit. Dijits are the official, accessible, themed components shipped with the Dojo Toolkit. It has its own namespace, and likewise its own collection of utility functions:
dijit.byId("firstWidget"); // is a reference to the actual widget.dijit.byId("firstWidget").domNode; // is the domNode the widget uses
// as opposed to:
dojo.byId("testHeading"); // is a domNode in our page
Using dijits
There are two ways to make Dijits: via markup, or programatically. The markup route breaks W3C validation because Dojo conveniently uses customized attributes in the markup to configure the widget. If this concerns you, it can all be done with script. We’ll do both.
Start by making a new skeleton file, including a couple changes for dijit styling: the default theme tundra’s CSS, and setting to enable it:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Dijit Test Page</title>
<link rel="stylesheet"
href="js/dojotoolkit/dijit/themes/tundra/tundra.css" />
<!– load the dojo toolkit base –>
<script type="text/javascript" src="js/dojotoolkit/dojo/dojo.js"
djConfig="parseOnLoad:true, isDebug:true"></script>
<script type="text/javascript">
// our code, and dojo.requires()
</script>
</head>
<body class="tundra"><!– this is a Typical WebPage starting point … –>
<h1 id="testHeading">Dijit Skeleton Page</h1>
<!– empty placeholder nodes –>
<div id="sampleNode"></div>
<div id="anotherNode"></div>
</body>
</html>
Dijit uses Dojo’s package system to track dependencies via dojo.require. Simply call in the modules you need in a script tag. For instance, to use a dijit.Dialog and dijit.form.Button, you need the following calls:
<script type="text/javascript">dojo.require("dijit.Dialog");
dojo.require("dijit.form.Button");
</script> The dijit.Dialog is a modal dialog box. It takes the node’s content, and displays it front-and-center on the viewport, awaiting user interaction. It can also act as a form element. To explore beyond this guide, visit the dijit.Dialog API Pages, or the Book overview.
From markup
You can specify all the attributes needed to setup your widget directly in markup, the most important being the dojoType. The parser finds the dojoType attribute, and turns the node into a Dijit with the matching classname. title is a common attrbute used by many widgets with headings:
<div dojoType="dijit.Dialog" id="sampleNode" title="The First Widget"><p>I am the Content inside the dialog.</p>
</div>
<button dojoType="dijit.form.Button" id="myButtom"
onClick="console.log(’clicked’)">
And Button
</button>
If parseOnLoad is true, the widgets will be created, then addOnLoad code will be executed. If you want to execute code before widgets are parsed, set parseOnLoad:false, and put your code inside an addOnLoad function as before. Issuing the command dojo.parser.parse(); will create the widgets when you are ready.
If parseOnLoad is true, the parser is loaded automatically. Otherwise, you must issue a dojo.require("dojo.parser"); call to include the required functions. All dijits use the parser, so it is included automatically.From JavaScript
The same results can be achieved using valid HTML and JavaScript. Our markup is simple, valid HTML:
<div id="sampleNode"><p>I am the Content inside the dialog.</p>
</div>
<button id="myButton">
Show Button
</button>
And our script is standard dojo code. We pass all the attributes as an object into our constructor, and tell it to use the node “sampleNode” for it’s content. All Dijits (or declared classes) can be created using the JavaScript new function.
dojo.require("dijit.Dialog");dojo.require("dijit.form.Button");
dojo.addOnLoad(function(){
// make the button
var theButton = new dijit.form.Button({
onClick:function(){
console.log("clicked");
}
},"myButton");
// make our Dialog
var theDijit = new dijit.Dialog({
title:"The First Widget"
},"sampleNode");
// make sure its started. parser does this if using markup
theDijit.startup();
});
When the button is clicked, you should see the word “clicked” in your Firebug (or Firebug Lite) console.
Manipulating The Widget
With our dialog successfully loaded and parsed (no errors were thrown, and the content of the Dialog is hidden), we need to explore some of the ways to manipulate the widgets. The function dijit.byId gives us a reference to our widget. The dijit.Dialog has an id of sampleNode.
To make the button the button control the dialog, modify the button’s onClick attribute to do more than print text:
<div dojoType="dijit.Dialog" id="sampleNode" title="The First Widget"><p>I am the Content inside the dialog.</p>
</div>
<button dojoType="dijit.form.Button" id="myButton"
onClick="dijit.byId(’sampleNode’).show()">
Show Dialog
</button>
If using the programmatic method, modify the lines that create the button:
// make the buttonvar theButton = new dijit.form.Button({
onClick:function(){
dijit.byId("sampleNode").show();
}
},"myButton");
The dijit.Dialog inherits from a dijit.layout.ContentPane which provides a few content-handling methods, including setHref. Add a new button outside the dialog with a new onClick function:
<div dojoType="dijit.Dialog" id="sampleNode" title="The First Widget"><p>I am the Content inside the dialog.</p>
</div>
<button dojoType="dijit.form.Button" id="myButton"
onClick="dijit.byId(’sampleNode’).show()">
Show Dialog
</button>
<button dojoType="dijit.form.Button" id="otherButton"
onClick="dijit.byId(’sampleNode’).setHref(’sample.txt’)">
Change Content
</button>
Or programatically by adding another button to our HTML:
<div id="sampleNode"><p>I am the Content inside the dialog.</p>
</div>
<button id="myButton">
Show Button
</button>
<button id="otherButton">
Change Dialog
</button>
And an additional new call:
// make the buttonvar theButton = new dijit.form.Button({
onClick:function(){
dijit.byId("sampleNode").show();
}
},"myButton");
var theButton = new dijit.form.Button({
onClick:function(){
dijit.byId("sampleNode").setHref("sample.txt");
}
},"otherButton")
Adding an id attribute to the paragraph inside the Dialog is an easy way to demonstrate another useful Dijit tool, dojo.getEnclosingWidget, to find which widget contains a passed domNode:
// show the dialog onLoad, without knowing it’s iddojo.addOnLoad(function(){
// add <p id="myPara"> to the dialog content
var p = dojo.byId("myPara");
var theDijit = dijit.getEnclosingWidget(p);
theDijit.show();
});
Related posts:
Ajax: Simple Transports
Ajax is an acronym for “Asynchronous JavaScript and XML”, a technology employed to send and receive data on the fly. It can be used to update sections of a website from any number of remote sources, send data to the server and pass responses back and forth, all without ever refreshing the webpage.
Having been versed on some essential Dojo methods, we’ll move on the the bread and butter of Ajax: XmlHttpRequest (or XHR for short). Dojo has several XHR methods available using common HTTP verbs: POST, GET, PUT, and DELETE.
To prepare, we need to create a file with some text to load in. Create a file named sample.txt in your js/ folder with sample text:
I am a remote file.We used Ajax to put this text
in our page.
And modify the skeleton.html to have some basic markup and style:
<style type="text/css">#container {
border:1px dotted #b7b7b7;
background:#ededed;
width:75px;
height:55px;
}
</style>
<div id="container" class="box">
<div id="content">
I am some Inner Content.
I am going to be replaced
</div>
</div> The XHR methods use dojo.Deferred behind the scenes to handle callbacks. This is beyond the scope of a QuickStart, but extremely useful in practice. If you would like to learn more about callbacks and the various way to set them up, visit the Dojo book or the dojo.Deferred API pages.
Getting data
The first stepping stone is dojo.xhrGet, which will return the contents of a GET call on a URL. The XHR methods share a lot of common parameters. Most important are the url: (our destination) and handleAs: (how we handle what is coming back). When the data arrives, it will be passed the the load: function we define:
var init = function(){var contentNode = dojo.byId("content");
dojo.xhrGet({
url: "js/sample.txt",
handleAs: "text",
load: function(data,args){
// fade out the node we’re modifying
dojo.fadeOut({
node: contentNode,
onEnd: function(){
// set the data, fade it back in
contentNode.innerHTML = data;
dojo.fadeIn({ node: contentNode }).play();
}
}).play();
},
// if any error occurs, it goes here:
error: function(error,args){
console.warn("error!",error);
}
});
};
dojo.addOnLoad(init);
You will notice we’ve combined techniques above. The content will fade out, be replaced by the received data, and fade back in using methods we’ve learned to this point. It was almost too easy.
A single handle argument can be used instead of load and error, handling both success and failure cases in a common function:
var init = function(){dojo.xhrGet({
url: "js/sample.txt",
handleAs: "text",
handle: function(data,args){
if(typeof data == "error"){
console.warn("error!");
console.log(args);
}else{
// the fade can be plugged in here, too
dojo.byId("content").innerHTML = data;
}
}
});
};
dojo.addOnLoad(init);
XHR has limitations. The big one being that url is not cross-domain. You can’t submit the request outside of the current host (eg: to url:”http://google.com”). It is a known limitation and a common mistake when getting excited about Ajax. Dojo provides alternatives like dojo.io.iframe and dojo.io.script for more advanced usage.
You also may experience problems with the Ajax samples if you are using Internet Explorer without a web server (from the local filesystem). It is a know security limitation of XHR and IE. While most of these examples do work from the filesystem, it is recommended you have a web server accessible to host the Dojo source, and your tests.A full list of XHR parameters is available at the API page, or in the Dojo Book. We are only going to skim the surface here.
Sending Data
All Dojo XHR methods are bi-directional. The only difference is the method. Using dojo.xhrPost, we use the POST method, embedding the data in the request (as opposed to the query string as with dojo.xhrGet). The data can be set directly as an object passed to the content parameter:
dojo.addOnLoad(function(){dojo.xhrPost({
url:"submit.html",
content: {
"key":"value",
"foo":42,
"bar": {
"baz" :"value"
}
},
load: function(data,ioargs){
console.log(data);
}
});
});
Or more commonly, conveniently converted from a form parameter. First, make a simple unobtrusive form in the skeleton.html:
<form id="mainForm" action="ajax-submit.php" method="post"><label for="firstName">Name: </label>
<input type="text" name="firstName" value="Enter Name" />
</form>
Then, add in some JavaScript to submit the form by using dojo.connect to listen to the onSubmit event, and post the contents of the form to an alternate URL:
// submit the formvar formSubmit = function(e){
// prevent the form from actually submitting
e.preventDefault();
// submit the form in the background
dojo.xhrPost({
url: "alternate-submit.php",
form: "mainForm",
handleAs: "text",
handle: function(data,args){
if(typeof data == "error"){
console.warn("error!",args);
}else{
// show our response
console.log(data);
}
}
});
};
dojo.addOnLoad(function(){
var theForm = dojo.byId("mainForm");
// another dojo.connect syntax: call a function directly
dojo.connect(theForm,"onsubmit","formSubmit");
}); Notice e.preventDefault() being used again. The default nature of a form being submitted to to visit a new page, and we want to prevent that from happening.
An example alternate-submit.php would look like:
<?phpprint "DATA RECEIVED:";
print "<ul>";
foreach($_REQUEST as $key => $var){
print "<li>".$key." = ".$var."</li>";
}
print "</ul>";
?>
Object Data
Getting text back from the server is nice, but the really great stuff comes when you start passing JavaScript objects around. Using a different handleAs: attribute, we can alter how Dojo handles the response data. Make a new file named simple-object.json to load:
{foo: "bar",
name: "SitePen",
aFunction: function(){
alert("internal function run");
},
nested: {
sub: "element",
another: "subelement"
}
}
We’ll target our xhrPost url: to the new file, and supply a handleAs: "json" parameter to convert the response data to an actual object we can use:
var postData = function(){dojo.xhrPost({
url: "js/simple-object.json",
handleAs: "json",
load: function(data,ioargs){
// success: set heading, run function
dojo.byId("testHeading").innerHTML += " by: "+data.name;
if(data.aFunction && data.aFunction()){
// we just ran data.aFunction(). should alert() …
}
}
});
};
dojo.addOnLoad(postData); A message will be thrown wanting you to use “json-comment-filtered” as a handleAs: value. You can either use the alternate value, or set your djConfig’s usePlainJson: true to deprecate this warning.
This allows us to send literally any kind of data back and forth across the wire, without ever interrupting the user experience.
Related posts:
Some Gloss: Dojo Animations
Dojo has a powerful animation system with several pre-made animations for a lot of common use cases. Adding some visual flair to you projects has never been easier, and typically makes the users experience a lot more interesting.
All animations use a single “magic object” as it’s only parameter. The most important being the node: attribute, the domNode on which to apply our animation. Some parameters are optional, and some are for advanced usage. A common setup would look something similar to:
dojo.addOnLoad(function(){var animArgs = {
node: "testHeading",
duration: 1000, // ms to run animation
delay: 250 // ms to stall before playing
};
dojo.fadeOut(animArgs).play();
});
Base Animations:
Animations included in base dojo.js are: fadeIn, fadeOut, and animateProperty. dojo.animateProperty is very powerful, and is the foundation for most advanced animations, and other animations in Dojo Core.
dojo.addOnLoad(function(){dojo.style("testHeading","opacity","0"); // hide it
var anim1 = dojo.fadeOut({ node: "testHeading", duration:700 });
var anim2 = dojo.animateProperty({
node: "testHeading", delay: 1000,
properties:{
// fade back in and make text bigger
opacity: { end: 1 }, fontSize: { end:19, unit:"pt"}
}
});
anim1.play();
anim2.play();
});
As seen, dojo.animateProperty will fade the element back in via it’s opacity property, and simultaneously make the text larger. You can animate most any CSS property this way.
In JavaScript, when modifying multi-word properties such as font-size and border-top, you must use a mixed cased version, as hypens are illegal as keys. Use fontSize and lineHeight, instead of font-size or line-height for example.Additional FX
A lot can be done visually with the base animations, animateProperty especially. To keep the size of the base dojo.js down, all the additional animations and tools have been packaged into a single module: dojo.fx to be optionally called in via dojo.require. Adding the module to your code provides several additional animation methods: dojo.fx.combine, dojo.fx.chain, dojo.fx.wipeIn, dojo.fx.wipeOut and dojo.fx.slideTo.
dojo.require("dojo.fx");dojo.addOnLoad(function(){
// slide the node to 75,75
dojo.fx.slideTo({
node:"testHeading",
top:75, left:75
}).play(); // and play it
});
dojo.fx.chain and dojo.fx.combine are very useful, too. They run animations in parallel or in sequence, returning a single instance of dojo._Animation to use:
dojo.require("dojo.fx");dojo.addOnLoad(function(){
var anim = dojo.fadeOut({ node: "testHeading" });
var anim2 = dojo.fadeIn({ node: "testHeading" });
dojo.fx.chain([anim,anim2]).play();
});
Combining an animation to fade in and out wouldn’t make sense, so lets fade it out and slide the node simultaneously using dojo.fx.combine:
dojo.require("dojo.fx");dojo.addOnLoad(function(){
var anim = dojo.fadeOut({ node: "testHeading" });
var anim2 = dojo.fx.slideTo({ node: "testHeading", top:75, left:75 });
var result = dojo.fx.combine([anim,anim2]);
result.play();
});
Animation Events
Each dojo._Animation has a series of “events” to tie into for more advanced usage. Going back to the one-stop-event-shop dojo.connect, we can connect to specific actions of the animation, and do other things. The most common are onEnd and beforeBegin:
dojo.addOnLoad(function(){var anim = dojo.fadeOut({ node: "testHeading" });
dojo.connect(anim,"onEnd",function(){
console.log(" the animation is done ");
});
dojo.connect(anim,"beforeBegin",function(){
console.log(" the animation is about to start ");
});
anim.play();
});
These events are especially helpful when you want to do things like change some content out while a node is hidden and then fade it back in:
dojo.addOnLoad(function(){var anim = dojo.fadeOut({ node: "testHeading" });
dojo.connect(anim,"onEnd",function(){
dojo.byId("testHeading").innerHTML = "replaced after fade!";
dojo.fadeIn({ node:"testHeading" }).play();
});
anim.play();
});
Conveniently, you can pass the event functions as properties to the animation. Using dojo.connect to setup the functions gives us a lot more power, and are typically safer for advanced uses, but sometimes it’s easier to wrap it all in:
dojo.addOnLoad(function(){var anim = dojo.fadeOut({
node: "testHeading",
onEnd: function(){
dojo.byId("testHeading").innerHTML = "replaced … ";
dojo.fadeIn({ node: "testHeading" }).play();
}
}).play();
});
The full explanation of events is available at the dojo._Animation API pages.
animateProperty
Probably the most powerful of the base animations, dojo.animateProperty allows us to easily animate multiple css properties simultaneously.
Since animateProperty is a dojo._Animation, it uses the same arguments as other animations. With an additional object, properties we can define any style property of a node. From start to end, and optionally using a unit attribute.
Manipulating our header element to use a new font color, size, and overall opacity is as easy as:
dojo.addOnLoad(function(){var anim = dojo.animateProperty({
node:"testHeading",
duration:700,
properties: {
// javascript css names are camelCase
// (not hyphenated)
fontSize: { start:12, end:22, unit:"pt" },
opacity: { start:1, end:0.5 },
color: { start: "#000", end:"#FFE" }
},
delay:100 // Note! trailing commas break IE.
});
anim.play();
});
dojo.query Animations
Dojo provides another convenient module: dojo.NodeList-fx, which adds additional methods to dojo.query for the available dojox.fx animations. To enable these methods, simply add in the required module:
dojo.require("dojo.NodeList-fx");dojo.addOnLoad(function(){
dojo.query("#testHeading").fadeOut().play();
});
The above gives us the same effect as calling dojo.fadeOut directly, but dojo.query here makes an animation for each of of the NodeList elements, and combines them into a single dojo._Animation. This can be useful when you have groups of like nodes you want to easily affect (in this case, all the nodes with class=”fadeNode”):
dojo.require("dojo.NodeList-fx");var fadeThem = function(){
dojo.query(".fadeNode").fadeOut().play();
}
dojo.addOnLoad(function(){
dojo.connect(dojo.byId("testHeading"),"onclick","fadeThem");
}); Unlike other dojo.query() chains, the NodeList-fx methods return an instance of dojo._Animation, preventing further chaining.
Related posts:
Events
The next important concept we are going to cover is interacting with our page. We’ve already set the heading to some alternate text, but what if we wanted to do something more interesting? Perhaps change it to something else when the user clicks on it? dojo.connect is the one-stop solution for all your event needs:
dojo.addOnLoad(function(){var node = dojo.byId("testHeading");
dojo.connect(node,"onclick",function(){
node.innerHTML = "I’ve been clicked";
});
});
A convenient way to do the above using dojo.query would be:
dojo.addOnLoad(function(){dojo.query("#testHeading")
.style("cursor","pointer")
.connect("onclick",function(){
this.innerHTML = "I’ve been clicked";
});
}); We added another chain .style to our example, to make the cursor a pointer when hovering over the header node. We could have done this with plain CSS, and probably should have, to avoid unnecessary code. This, however, is a convenient way to dynamically alter most any CSS property, and very useful.
This allows us to make an onclick function on more than one node at a time, though our NodeList only has one element above. We could easily find a big group of elements, and affect them all. For instance, to prevent all links on a page from leaving, utilize the normalized event object dojo.connect passes:
var disableLinks = function(){dojo.query("a").connect("onclick",function(e){
e.preventDefault(); // stop the event
console.log(’clicked: ‘,e.target); // the node we clicked on
});
};
dojo.addOnLoad(disableLinks);
The code e.preventDefault will prevent the event from “doing what it was going to do”. In the example, we preventDefault on the click event, which would have followed the anchor link we connected to. It is common to see the event object written as e or evt when passed as a paramter.
More about the normalized event object used in Dojo can be found in the Event Object Book Page at the Dojo website.We can also connect to methods of specific objects, and execute them in the same scope. This is useful as you get into declaring classes in Dijit, or animations. Lets create a really simple object with some methods, and watch them interact:
var mineObj = {aMethod: function(){
console.log(’running A’);
},
bMethod: function(){
console.log(’running B’);
}
};
var otherObj = {
cMethod: function(){
console.log(’running C’);
}
};
dojo.addOnLoad(function(){
// run bMethod() whenever aMethod() gets run
dojo.connect(mineObj,"aMethod",mineObj,"bMethod");
// run an entirely different object’s method via a separate connection
dojo.connect(mineObj,"bMethod",otherObj,"cMethod");
// start chain of events
mineObj.aMethod();
});
You should see “running A B C” on separate lines in the console. The full power of dojo.connect can be explored via the dojo.connect API, or in the Events chapter of the Dojo Book.
Related posts:
DOM Magic
A really nice tool Dojo provides is dojo.query. It’s a great way to parse all or portions of the Document Object Model (DOM) and access selections of nodes. It really deserves its own book. Each of the following sections will touch on how to use dojo.query more closely, though realizing its potential is as simple as seeing it used:
- dojo.require("dojo.NodeList-fx");
- dojo.addOnLoad(function(){
- // our dom is ready, get the node:
- dojo.query("#testHeading")
- .addClass("testClass") // adds class="testClass"
- .fadeOut({ delay:500 }).play(); // and fade it out after 500 ms
- });
Add .testClass CSS definitions to set color:red; and your heading will get that style before fading out:
- .testClass {
- color:#ff0000;
- }
dojo.query returns an instance of a dojo.NodeList, a synthetic super-Array of domNodes. It supports most CSS3 selectors (so you can go really wild with its syntax), and execute code against the whole list of results. To demonstrate this, we’re going to need something more than a single heading, so add some content to our DOM:
- h1 id="testHeading">Dojo Skeleton Page</h1>
- <a class="link" href="#">First link</a>
- <a class="link" href="#">Second Link</a>
- <p class="para">First paragraph</p>
- <p class="para">Second paragraph</p>
- <p class="para">Third paragraph</p>
And use a different query:
- dojo.require("dojo.NodeList-fx");
- dojo.addOnLoad(function(){
- // get each element with class="para"
- dojo.query(".para")
- .addClass("testClass")
- .fadeOut({ delay: 1000 }).play();
- });
All three
elements should turn red, and fade out after a second delay. The full list of things dojo.NodeList does is impressive, some of which we’ll touch on in later sections of this guide.
Most dojo.query chains have standalone functions to achieve the same goals. For instance: dojo.query(”#testHeading”).addClass(”testClass”); and dojo.addClass(”testHeading”,”testClass”) have identical results.Related posts:
First Steps
Start by making a skeleton HTML file for use as a basic template for any example:
- <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
- "http://www.w3.org/TR/html4/strict.dtd">
- <html>
- <head>
- <title>Dojo Toolkit Test Page</title>
- <!– load the dojo toolkit base –>
- <script type="text/javascript" src="js/dojotoolkit/dojo/dojo.js"
- djConfig="parseOnLoad:true, isDebug:true"></script>
- <script type="text/javascript">
- /* our JavaScript will go here */
- </script>
- <style type="text/css">
- /* our CSS can go here */
- </style>
- </head>
- <body><!– this is a Typical WebPage starting point … –>
- <h1 id="testHeading">Dojo Skeleton Page</h1>
- <div id="contentNode">
- <p>Some Content To Replace</p>
- </div>
- </body>
- </html>
This page has a DOCTYPE of “HTML/4.01 Strict”, and almost passes W3C validation. This can be fixed, but the shorthand is convenient. You will learn about both valid and convenient methods in this guide.
Configuring Dojo
Dojo has a mechanism for setting various configuration options at runtime. The two most common are parseOnLoad, which toggles page-load parsing of widgets and in-markup code, and isDebug, which enables or disables certain debugging messages.
Conveniently, you can set these options directly in the tag that loads in dojo.js via a custom attribute named djConfig. Simply modify the skeleton HTML template to add the new attribute:
- <script type="text/javascript" src="js/dojotoolkit/dojo/dojo.js"
- djConfig="parseOnLoad:true, isDebug:true"></script>
If the above validation concerns you (you know who you are), you can setup a global djConfig variable before dojo.js is loaded:
- <script type="text/javascript">
- var djConfig = {
- isDebug:true,
- parseOnLoad:true
- };
- </script>
- <script type="text/javascript" src="js/dojotoolkit/dojo/dojo.js"></script>
Both examples have the same effect.
When can I start?
As soon as the document is ready and loaded…
There are a number of cross-browser differences in defining “ready”, so to aid in your continued sanity, Dojo has a method of executing code when the document is really “ready”: dojo.addOnLoad. Everything we do that could possibly affect the DOM should be started by passing dojo.addOnLoad a function:
- // a very common method of loading code onLoad
- var init = function(){
- console.log("I run after the page is ready.");
- };
- dojo.addOnLoad(init);
- // and/or pass an anonymous function
- dojo.addOnLoad(function(){
- console.log("I also run, but second. ");
- });
dojo.addOnLoad is a fundamental aspect of using Dojo, and is very important to remember. Without it, you cannot be sure all the necessary content has been loaded before your own code begins to execute.
It’s important to note that you should not set on onLoad function directly on the tag when using dojo. dojo.addOnLoad(someFunc) is preferred over and window.onload = someFunc;More than just dojo
Dojo has a package system built in to load all the code you need, controlled by dojo.require(). This function allows us to pull in parts of the Dojo Toolkit not provided for in the Base dojo.js, such as Drag and Drop, additional animations, dijit widgets, dojox projects, or even your own code.
For example, to load the code needed to use the TitlePane widget, and a Dijit Button into your page include the modules dijit.TitlePane and dijit.form.Button:
- dojo.require("dijit.form.Button");
- dojo.require("dijit.TitlePane");
- dojo.addOnLoad(function(){
- dojo.byId("testHeading").innerHTML = "We’re on our way!";
- console.log("onLoad fires after require() is done");
- });
Each “module” has it’s own dojo.require()’s, and knows not to load code it already has. Code executed by dojo.addOnLoad doesn’t run until after your dojo.require()’s are all finished loading, making it that much safer and convenient to use.
A full list of available widgets and the module they live in can be found at the Dijit API pages, or explored by browsing the dijit/tests/ folder that came with your download.
Moving on
In the last example, we snuck a very common method into our addOnLoad code: dojo.byId(). This returns the domNode of an element by its id attribute. dojo.byId() is a convenient way to access a specific node, and manipulate it. Here we’re changing the text of the heading in the body, through its .innerHTML property.
If all you see is “We’re on our way”, you really are on your way to some really interesting web development: dojo.bliss. If you are experiencing errors, something has gone wrong. A lot of common mistakes are covered in the FAQ, available at the Dojo Toolkit website.
Related posts:
Getting the Code
Download the newest released version of the Dojo Toolkit from: http://download.dojotoolkit.org/
The “built” version is: dojo-release-1.3.0.zip
Unpack the contents of the archive into a folder (preferably on a web server as this is always a good case for Ajax development). Let’s call it “js/”. You may also name your dojo directory “dojotoolkit” as the examples here will show. If you wish to version Dojo, you may leave it as dojo-release-1.3.0. You should now have a directory structure similar to this:

The most important thing to know when installing the Dojo Toolkit is where your copy of dojo.js is located. The package system handles the loading of all other dependencies and modules, once dojo.js has been loaded into the page.
You can verify your download and install is working by pointing your web browser to http://localhost/js/dojotoolkit/dojo/tests/runTests.html or browse the dijit
test pages at http://localhost/js/dojotoolkit/dijit/tests/
The Dojo Book, a freely available collection of guides and tutorials, provides a more in-depth description of the various ways to get the Dojo source and about the different releases available.
Related posts:
Why Dojo?
The Dojo Toolkit is an open-source JavaScript toolkit useful for building great web applications. It aims to shorten the timespan between idea and implementation by providing an exceptionally well conceived API and set of tools for assisting and fixing the issues experienced in everyday web development. It is lightning fast, extremely robust, and supplies a solid set of tools for DOM manipulation, animations, Ajax, event and keyboard normalization, internationalization (i18n) and accessibility (a11y). Dojo Base is a single, lightweight 26KB entity “across the wire.” Dojo is completely free, liberally licensed (AFL or BSD), and transparently developed by an active group of developers with a strong community presence.
No matter the size of your projects, Dojo is the ultimate scalable solution to your development woes. The built-in package system ends the headache of tracking dependencies, the build system combines and shrinks optimized “layers” of code for deployment, and D.O.H. makes unit and regression testing a breeze.
The add-ons
In addition to all the great tools available in the Base dojo.js, using the powerful package system, you can add functionality to your application through simple dojo.require() calls. Dojo Core includes great tools such as drag and drop, advanced Ajax transports, string utilities, a powerful data API, and hundreds of others to use to easily make exceptional rich internet applications.
Endless possibilities
The Dojo Toolkit also comes pre-packaged with a project called Dijit, a system for using and creating encapsulated, reusable components or widgets. The system provides accessible, extensible, themeable components to drop into your web applications and sites, and a solid API for streamlining the development of your own widgets or customizing the behavior of existing widgets.
Cutting edge technology
With Dojo, many of the latest and greatest technologies are at your fingertips. Buzz words like Web 2.0, Ajax, and Comet provide a great starting point to describe the experience you’ll be creating. dojox.gfx, dojox.charting and Dojo Offline quickly go beyond the hype, pushing the limits of the web experience to new heights.
Related posts: