Home  >  

Welcome To The Dojo

Author photo
| | Comments (13)
AddThis Social Bookmark Button

Welcome To The Dojo

dojo.png

That is how a friend of mine introduced me to this Javascript Framework. "The Dojo? Great, now he's going to act all mystic like and not directly answer any of my questions." I thought to myself. Turns out, he was talking about a framework that is truly awesome. I'll admit, the documentation is a bit to dig through, but hopefully my experience and previous digging will be of use to you. Don't worry, the only things being thrown around in this "Dojo" are JavaScript and web programming ideas you may or may not have seen before.

In this article I would like to accomplish two major things: giving an introduction to the Dojo JavaScript Framework and a walk-through of how I approach creating a RIA using dynamic HTML and Dojo.

What is Dojo Anyhow?

For those of you already familiar with JavaScript libraries, the best way to describe Dojo is that it combines key aspects of all the specialized libraries into one namespace and tacks on a boatload of useful widgets. You get the kind of DOM manipulation abilities of JQuery and Prototype, superbly developed animation effects like Scriptaculous, and widgets that pull it all together. For those of you who are not familiar with any libraries, Dojo is a one stop shop to "Ajaxify" (a term completely fabricated by one of our project managers) your applications.

The biggest thing I like about Dojo is that the syntax of everything just makes sense. The developers keep to the convention that every variable/property (exposed variables anyhow) and functions have descriptive names of what they do. I know a lot of people like JQuery and Prototype for the shortcuts they allow you to do, but I remember the first time I tried to look at code in those languages I was greatly confused by what was going on. Sure, it makes it handy to quickly churn something out, but you have to mentally compile stuff when you look back at something you did six months ago. Besides, a lot of the syntax around the shortcuts reminded me of Perl, and I can't stand Perl :).

Dojo is distributed a few ways in a few forms. One way is to download the framework and put it in your project. If you choose this way, they have it available in two versions: a development version and a release version. The only difference is that the development version is readable and the release has been minified. In production, it is wise to use the minified version, but I usually have both copies on my system so I can refer back to the readable version when I'm trying to figure out how something works.

The other way to enjoy the Dojo goodness is to include it from AOL's or Google's Content Delivery Network.

 
AOL: <SCRIPT TYPE="text/javascript" SRC="http://o.aolcdn.com/dojo/1.3/dojo/dojo.xd.js"></SCRIPT> 
Google: <SCRIPT TYPE="text/javascript" SRC="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js"></SCRIPT> 

You will want to check back with Dojo's site for new links as they update the versions.

Dojo, Dijit, and DojoX

Ok, so you've gone to Dojo's site to download the framework only to be greeted by the above options. "What is all of this?" you may be asking yourself. Glad you asked! Dojo, Dijit, and DojoX are the top level packages of Dojo.

The Dojo package contains all of the basic functionality of the framework and needs to be included regardless of the other packages. Some of the features here include things like DOM manipulation tools, data and IO functions, the animation libraries, and all of the Xhr functions (Ajax Post and Get calls).

The Dijit package expands on the functionality in the Dojo package by combining things into useful widgets. These things would include useful tools like Dialog Boxes, various form controls (the filtering select is a very cool one), tree objects, context menus, and some very useful layout controls we will explore later in this article.

The DojoX package contains high level widgets and other things that could be considered applications in their own right. It is here that you get things like highly customizable sorting datagrids, easing for effects, code highlighting, and much more.

Also, it is worth mentioning that they have taken the trouble to make sure all of their functionality is browser agnostic - meaning that they have taken into account all of the funky quirks of all the browsers out there. This means when you want to do something like grab the coordinates of a HTML element, there is only one function call to pull this off. This is awesome because all browsers have slightly different ways of calculating the offset of elements. Some use the client width and height, some use the window, and still others use the document element for calculations. Did I mention that some of the properties each browser uses aren't available in the other browsers (and thus will break your Javascript).

Another example is something I've run into a lot building nice UI elements. Firefox and IE handle opacity a bit differently. For Firefox, you use an opacity style element with floating point values from 0 to 1 and in IE you use a filter (filter:alpha(opacity=#)) with values from 0 to 100. The quirky thing is that if you start that filter off at 0 for IE, no matter what you do, you can't ever RAISE or change it past that point. However, if you use Dojo to set this dynamically before running any effects, you can change it to your heart's content. A simple call to dojo.fadeOut will lower the opacity of your element to whatever level you want, in whatever BROWSER you want.

Behold, the power of Dojo! :)

Ok, How Do I Use This?

By this point you have either moved on to something else that shows you code, or you are sitting there eagerly awaiting more Dojo goodness. Your patience will be rewarded my friend, and we will start getting in to a basic RIA application/site using Dojo to manage the UI.

Our example site will be pretty basic and consist of a header, navigation panel, and a content area. Our requirements will be to have the content area dynamically update as the user navigates the page without a page refresh and an ad/informational block change in the header on every click of the main navigation. To accomplish this, we will be using pieces of Dojo and Dijit.

Before we get started, lets discuss the layout a bit. I approach web applications like I would a desktop application, specifically a Java Swing application because it uses a lot of the same terminology. There are usually three parts of an application: static info areas (includes some of the look and feel), a control area, and a content area. The static pieces you can generally leave to your layout to take care of. The content area I like to think of as its own container, much like a container in Java Swing. These containers can be independently updated by different aspects of the application. The control area can be dynamic or static depending on the complexity of the page, but I generally think of it as a container that doesn't get updated by anything but itself. As we get into this sample site, this will make more sense.

To set up our application, I've used the following skeletal structure:

 
<html>
  <head>
    <title>Welcome To The Dojo</title>
    <meta content="">
    <style>
      #header
      {
 	width:900px;
 	height:150px;
 	background-color:#ccccff;
       }
      #nav
      {
 	width:200px;
 	height:500px;
 	float:left;
 	border-right:1px solid gray;
 	border-top:1px solid gray;
 	background-color:#ddddff;
       }
      #nav a
      {
 	margin-left:20px;
       }
      #content
      {
 	width:697px;
 	height:500px;
 	float:left;
 	border:1px solid gray;
       }
      #ads
      {
 	width:300px;
 	height:100px;
 	float:right;
 	margin-right:25px;
 	background-color:white;
       }
      #header label
      {
 	font-weight:bold;
 	font-size:22px;
       }
    </style>
    <style>
      @import "http://ajax.googleapis.com/ajax/libs/dojo/1.3/dijit/themes/tundra/tundra.css";
    </style>
    <SCRIPT TYPE="text/javascript" SRC="http://ajax.googleapis.com/ajax/libs/dojo/1.3/dojo/dojo.xd.js" djConfig="parseOnLoad: true"></SCRIPT>
  </head>
  <body class="tundra">
    <div id="header">
      <p>
	<label>Welcome to the Dojo</label><br>
      <p>
      <div>
	<div id="ads">
	  
	</div>
      </div>
    </div>
    <div id="nav">

    </div>
    <div id="content">
    
    </div>
  </body>
</html>

Lets discuss a bit what I've done here. First off, we need to include Dojo in our project - and this can be done in either of the two ways described above. For this example, I'm going to use Google's CDN. Also, you will notice there is a style tag that imports the "tundra.css" file from the CDN. This is one of Dojo's built in themes, and makes all of the widgets and such look much nicer. To make sure that all the components on the page use the theme, simply set the class to your body to "tundra" (or whatever theme you decide to use). Next, I've set up a few logical content areas using divs with specific ID values. This is important because the id is how we will reference this element in the future.

Now, I want to go ahead and treat one of these divs as an actual container. With Dojo, this is done using a widget in Dijit called a content pane. To do this, we first need to tell Dojo that we want to use it.

 
<script>
      dojo.require("dijit.layout.ContentPane");
</script>

By adding these lines to the header, we have told Dojo that we want to include the ContentPane widget into this page. That's it. By the beauty of Dojo's abstraction, it knows to go out to the CDN and download the additional files we need to make this work. Because of this, if in the future you decide to download Dojo to your computer and change the page to use the local copy, all you would need to change in your project is the initial load of the framework to point there. Nothing else in your project needs to change.

Next, we need to actually tell Dojo that one of our divs should be treated as a ContentPane. I want to take a step back for a second and explain one of the cooler aspects of Dojo. Almost ALL of the widgets can be created using pure JS or markup in the HTML. It accomplishes the latter by running a parser (see the parseOnLoad = true) on the HTML to look for special Dojo attributes on elements. Back to our project, we can tell Dojo that we want the "content" div to be a ContentPane by adding a dojoType attribute to it.

 
<div id="content" dojoType="dijit.layout.ContentPane">
    
</div>

You'll notice that the dojoType is the same as the package name for the widget you want to assign to it. Cool huh? :).

One thing to note here is that by assigning ID values to the bits and pieces we want to have access to, we can grab them from the DOM by calling dojo.byId("content"). For widgets like the ContentPane we just created, we can get to it by using the Dijit equivalent of that function: dijit.byId("content") This function also works for any DojoX widgets you use. We will be using that more later in this example.

dijit.layout.ContentPane

I think I should stop and talk a bit about what the ContentPane is and how you can interact with it. For the most part, it is just whatever element you've created with a bunch of extra functionality attached to it. In our case, it is attached to a div. We are going to be updating the content dynamically, and some of you may be thinking that you could just use the innerHTML property of the element to do that without all the fancy framework. It is true that you can in fact do that, but you have to get the content from somewhere.

One of the cool things you can do with the ContentPane is have it go get its own data. All you have to do is give it a URL and it will make the call to go grab the data and update its own innerHTML. For example, lets say you want to go grab foo.html from your site and display it in this content pane, the code to do so would look like:

 
dijit.byId("content").attr("href", "foo.html");

Likewise, you can change the loading message that the ContentPane shows by assigning the HTML to that attribute:

 
dijit.byId("content").attr("loadingMessage", "<b>LOADING!!!</b>");

You can also do more advanced things by handling the callbacks for your content pane. The ones I use the most are onLoad and onUnload, but it includes some special ones like onDownloadStart and onDownloadEnd. For these events, you can either give it a function you've already created, or feed it an inline function (closure).

 
dijit.byId("content").attr("onLoad", myfcn);
//or
dijit.byId("content").attr("onLoad", function(){
   alert("loaded!");
});

Tying it All Together

Looking back at our example, we want to now add a couple links in the nav div to load some dynamic content in our content div. We are going to do this by putting two anchor tags in with an onclick event to load our content. With these additions, our new nav div looks like this:

 
<div id="nav">
    <a href="foo.html" onclick="dijit.byId('content').attr('href', 'foo.html');return false">Foo</a><br>
    <a href="bar.html" onclick="dijit.byId('content').attr('href', 'bar.html');return false">Bar</a><br>
</div>

There really isn't anything special about these tags except for one key item. The end of the onclick js string has a "return false." Why is that you ask? Simple - to prevent your browser from actually following the anchor tag. With "return false", the browser will execute the Javascript without following the action of the anchor tag. We could just put the action in the href with a "javascript:" directive, but I have a reason for not doing things that way that we will get to later.

Now that we have the first part of our little project working, lets look at the second. We need to make the ad area change on each nav click. This will work in much the same way as above since the ad block will be a contentPane. The difference here is that we want the ads to rotate, so the source will be pulled from a Javascript array.

 
<div id="ads" dojoType="dijit.layout.ContentPane">
	  
</div>

Step one, create an array with some urls in it:

 
var adArray = ["ad1.html",
	       "ad2.html",
	       "ad3.html",
	       "ad4.html"];
var currentAdIndex = 0;

Now, we need to set up a function that will actually do the ad rotation:

 
function getNextAd()
{
   if(currentAdIndex < adArray.length - 1)
   {
  	  currentAdIndex++;
    }
   else
   {
  	  currentAdIndex = 0;
    }
   dijit.byId("ads").attr("href", adArray[currentAdIndex]);
}

Now, we need to set up the contentPane to load the first ad on page load. To do this, we will want to use the dojo.addOnLoad function to chain the contentPane load to happen after Dojo has loaded and done everything it needs to do. Unlinke the normal onload event, the dojo.addOnLoad function essentially appends what you pass to it to the normal onload event, ensuring that the code here loads AFTER the regular onload event - which lets us load special functionality to Dojo widgets AFTER they have loaded.

 
dojo.addOnLoad(function(){
   dijit.byId("ads").attr("onLoad", function(){
      dojo.fadeIn({node: dojo.byId("ads"),duration: 500}).play();
    });
   dojo.fadeOut({
      node: dojo.byId("ads"),
      duration: 1,
      onEnd:function(){
         dijit.byId("ads").attr("href", adArray[currentAdIndex]);
       }
    }).play();
});

You'll notice I did some extra things here. In addition to loading the first ad on page load, this function fades the ad block out, and adds an onload event to the contentPane to fade the ad box in after the content is loaded. Also, notice that I used another custom event of the dojo.fadeOut function - the onEnd event. This event gets fired after the animation ends, so in this case after the ad box had been faded out, it loads the ad content.

The final thing to do is to rotate the ad box on nav click. All we have to do for this is add an additional function call to the onclick events of our anchor tags. Lets define the function first:

 
function showNextAd()
{
   dojo.fadeOut({
      node: dojo.byId("ads"),
      duration: 1,
      onEnd:function(){
         getNextAd();
       }
    }).play();
}

You'll notice that this is much the same as the load function in our addOnLoad closure. The only difference is that I'm actually calling the getNextAd function to properly rotate the ads instead of grabbing the first one.

Finally, lets add this new function to our anchor tags:

 
<div id="nav">
  <a href="foo.html" onclick="dijit.byId('content').attr('href', 'foo.html');showNextAd();return false">Foo</a><br>
  <a href="bar.html" onclick="dijit.byId('content').attr('href', 'bar.html');showNextAd();return false">Bar</a><br>
</div>

Now, if you click our links, the content is loaded through an Xhr request and the ad rotates how we intended. The only problem is that this looks REALLY ugly and could be a copy/paste mess if you need to add more links. Thankfully, there is an easy solution - let Dojo do it for you!

Dynamic onclick

Dynamically adding onclick functions to elements is one of my favorite tricks when designing sites. Not only is it super easy, it cuts down on the amount of actual markup you have to deal with. We are going to pull this off by using the dojo.query function. This function allows you to use css element queries on your page and interact with the results (that come back in an array).

The function in question would look like this:

 
function addCallbacks(node, fcn)
{
   dojo.query("a", node).forEach(function(qnode)
   {
  	  qnode.onclick = function(event){fcn(qnode);return false;};
  	  
    });
}

What this function does is look for every "a" element in the "node" you tell it to look. Then, for each element it finds, it adds an onclick event to call the function you passed in. It even puts on our magic "return false" to prevent the browser from following the link. This little trick isn't over yet, however. The last part is to create the function you are passing to the addCallbacks function.

 
function loadContent(node)
  {
     dijit.byId('content').attr('href', node.href);
     showNextAd();
   }

This function takes a node (in this case, our anchor tag), and loads our main contentPane by using the href of the node and swaps out the ad. This is why I left our anchor tags having the href pointing to our file instead of a "javascript:" reference. The final thing to do here is to call our addCallbacks function in our addOnLoad function:

 
addCallbacks(dojo.byId("nav"), loadContent);

Like I said before, this makes adding new pages to our site that have the same functionality super easy. To test this, lets go ahead and add a few more pages. Our new nav block should look like this:

 
<div id="nav">
  <a href="foo.html">Foo</a><br>
  <a href="bar.html">Bar</a><br>
  <a href="test1.html">Test1</a><br>
  <a href="test2.html">Test2</a><br>
  <a href="test3.html">Test3</a><br>
</div>

There you have it. I hope these concepts have given you a good introduction of the types of things you can pull off with Dojo. Stay tuned for more Dojo goodness.

Run Example
View Index Source
Download Full Source

Read more from John Barlow. John Barlow's Atom feed _jnbarlow on Twitter

Comments

13 Comments

Doug said:

DOJO sucks; use Jquery!

We have Peter Higgins of the Dojo core dev team on FLOSS Weekly in a few weeks (http://twit.tv/floss).

Anonymous said:

jQuery is a nice toy

Alan Queen said:

Great article, well written.

John Barlow said:

Thanks Alan, I appreciate it!

Dojo Guy said:

Doug, Dojo is awesome!

Dojo has performance, scaleability, depth and breadth jQuery can dream about.

In any case, if you like jQuery, you will feel at home when you switch to Dojo. Dojo 1.4 has many (if not most) of the jQuery idioms that you like, but Dojo performance and robustness is way better.

This would have been an excellent article except for the dig against jQuery and Prototype. Why was that necessary? The complaint that the first time he was looking at code in those languages he was confused at what was going on is just silly. As if Dojo magically explains in great detail what the code is doing using coding styles and terms very similar to what the "confusing" languages use. Dojo Guy's post supports my point.

I personally hate all this fanboy "my stuff is better than your stuff!" arguments we keep seeing. These are tools and like most tools each has their own place. A good developer uses the tool best suited for the job at hand. But this Dojo has better whatever over jQuery nonsense is childish in nature and does nothing to further our industry.

I'm assuming most of us in this industry are adults but a good number of them refuse to act like it. People should just grow up.

John Barlow said:

Travis,

Thank you for your candor. I would, however, have to point out that I didn't "dig" against jQuery and Prototype. I clearly stated that all three frameworks have places, and that I chose to work with Dojo simply because I liked the syntax and features over the others. One of my co-workers uses Prototype for almost everything he does and we are always comparing projects. Some things could have been done easier in either framework, but it is always good to have a different perspective on things.

Ultimately, that was the goal of this article -- to give a different perspective on how to do things using Dojo. Dojo vs. jQuery, Mac vs. Windows, etc., it all doesn't matter to me. Use the tool that is easiest for you and gets the job done. :)

Hi John,

Fun article. Thought I would point out a few notes. Most [if not all] Dojo functions can accept a string Id in place of a dojo.byId call. The node: member for animations in particular.

dojo.fadeOut({ node:"ads" }).play() over dojo.fadeOut({ node: dojo.byId("ads") }).play().

Same with query context parameter:

dojo.query(".bar", "foo") is identical to dojo.query(".bar", dojo.byId("foo"))

The onclick example is needless verbose, and inadvertently creates an expando (and clobbers any other onclick functions for that particular node. You can just use NodeList's .onclick function:

dojo.query("a", node).onclick(fcn);

Though in your example you are passing the element to the callback in the first arg position. dojo.partial can do this for you, though you need a reference to it, so use forEach

dojo.query("a", node).forEach(function(n){
dojo.connect(n, "onclick", dojo.partial(fcn, n));
});

or know that the .onclick function [without adjusting the scope] sets `this` to be the element.

Regards

@john - I apologize, rereading my comment I can see that it didn't exactly say what I intended. Only my first paragraph referred to your article. The rest was just aimed at fanboyism in general much like Dojo Guy's post. As I said, each tool has their place and his comment was nothing other than "mine is better than yours", which is not needed. But I stand by my comment that you did in fact had an unnecessary negative (granted, slight) comment toward the other frameworks. I just wanted to know what Dojo could do for me as a developer. If your article did not have the entire paragraph about why you didn't like jQuery or Prototype then the article would have been better for it.

For me, this article is wonderful. I've created many applications with DOJO. I find it to be awesome because of the way it handles Multiple inheritance. DOJO makes Javascript OOP possible. I'm able to complete front-end and back-end development for multiple projects from different clients simply by reusing my super classes over and over again.

If a web developer has never used OOP techniques, then they probably won't understand why DOJO is so powerful.

In a nut shell DOJO is making me more money by allowing me to take on more clients with less effort.....everybody has an opinion, but money talks.

For me, this article is wonderful. I've created many applications with DOJO. I find it to be awesome because of the way it handles Multiple inheritance. DOJO makes Javascript OOP possible. I'm able to complete front-end and back-end development for multiple projects from different clients simply by reusing my super classes over and over again.

If a web developer has never used OOP techniques, then they probably won't understand why DOJO is so powerful.

In a nut shell DOJO is making me more money by allowing me to take on more clients with less effort.....everybody has an opinion, but money talks.

Leave a comment


Tag Cloud

Question of the Week: Call for Topics

What do you want from InsideRIA?

Answer

Latest Features

Recommended for You

@InsideRIA on Twitter

Archives

  • Or, visit our complete archive.  

About This Site

Welcome to the premiere community site for all things RIA sponsored by O'Reilly Media and Adobe Systems Incorporated.