Maybe you saw that tweet: “jQuery is a gateway drug. It leads to full-on JavaScript usage.” Part of that addiction, I contend, is learning other JavaScript frameworks. And that’s what this four-part series on the incredible Dojo Toolkit is all about: taking you to the next level of your JavaScript addiction.
Before We Begin
I should mention before we begin that the only prerequisite for this series is that you have at least a basic knowledge of JavaScript. If you’ve used another JS library before, you’ll be even better off. But, although I compare Dojo to jQuery a couple of times, you don’t need to know jQuery to be comfy in this class.
Prefer Visual Training?
And, one more thing: I'll be producing a screencast for each one of these tutorials, covering everything in the written tutorials, and maybe a little bit more. The 'casts are part of the Net Premium Subscription, so if you aren't a member, sign up to get them and a metric ton of other incredible Premium content.
Meeting Dojo
Dojo is officially called the Dojo Toolkit. This is actually very fitting. Most other collections of lines of JavaScript available bill themselves as frameworks or libraries. In my mind, a framework is a more or less end-to-end solution for building good web applications, and a library is a collection of tools that assist you with a few specific (usually related) tasks. Dojo fits into both categories, and then some. It’s got all the DOM manipulation, events and animation helpers, and AJAX functions that you’d get with a library like jQuery. But there’s more, much more.
On your first few dates with Dojo, you won’t realize just how much there is to it. So, let me introduce you to the three main parts of Dojo:
- Dojo Core: Dojo Core is the main, base functionality. Most of it is the kind of thing you’d get with jQuery. However, it also holds dozens of general-purpose language utilities, as well as the plumbing for the other parts of Dojo.
- Dijit: Dijit is the UI library of Dojo; it’s an official sub-project, managed by separate people. In that way, it’s similar to jQuery UI. A lot of the functionality is similar to the kind of things you’d find in the jQuery UI Widgets library as well: Calendar pickers, combo boxes, and buttons. If you want to crank up your web forms a notch, you’ll find almost everything you need in Dijit. Dijit also contains some interesting layout tools.
- DojoX: DojoX (Dojo extensions) is a collection of individual projects that, you guessed it, extend Dojo. It’s hardly an exaggeration to say that, “there a Dojo extension for that.” Incredible charting utilities? Check. Every type of data store you’d ever want, and then some? You bet. Even more form helpers to boost the ones available in Dijit? It’s here. It’s all here.
Getting Dojo
We’ll start, of course, by getting Dojo on the page. I want to tell you that Dojo isn’t like jQuery, because there are dozens of files that make up Dojo, Dijit, and Dojox. The reason I’m hesitant to say this is that you’ll say that jQuery isn’t just one file: there are all the plugins and extensions that are made for it. The difference with Dojo is that, all these extra parts are officially part of Dojo, and can be called into your webpage at any time.
However, right now, we only need the Dojo Base. This is a subset of Dojo Core, available in a single file. So, while you can download all of Dojo (and Digit and Dojox), or create custom builds of it with just the parts of it you want, we’re going to take the easy route and get the Base from the the Google CDN.
<script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js"></script>
So, create an index.html
file and start with this little template:
<html> <head> <title> Intro to Dojo, part 1 </title> <style> .highlight { background: yellow; font-weight: bold; font-size: 1.5em; } </style> </head> <body> <h1> A Heading </h1> <ul id="nav"> <li> <a href="/">Home</a> </li> <li class="highlight"> <a href="/portfolio"> Portfolio </a> </li> <li> <a href="/about">Abou</a> </li> <li> <a href="/contact">Contact</a> </li> </ul> <p> This is a paragraph (albeit a very <em>short</em> paragraph). Also, <a href="http://google.com">here's a link</a>. </p> <script src="http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js"></script> </body> </html>
I’ve included a bunch of elements in this little demo page. We’ll be using them as we explore Dojo.
I’ll mention one more thing before we get started: when you’re learning a library like Dojo, you’ll probably find it useful to view our testing page in your browser of choice and pop open the respective JavaScript console. Take any line of code in this tutorial and paste it into the console, and you’ll see what’s happening.
Finding Elements
In this tutorial, we’re going to be learning Dojo primarily as a replacement for jQuery, or whatever DOM-focused library you use. Of course, that’s hardly a floorboard in this Titanic, but it’s a good place to start. Once you’re comfortable using it in place of your normal library, we can go on to what makes Dojo unique.
The usual M.O. with these things is get it, use it; so, let’s start with finding DOM elements.
Dojo has a couple of methods for hunting through the DOM. The first one we’ll look at is dojo.query
, which is very much like the jQuery
(or $
) method. Just pass it a CSS selector string, and it will find all the elements in your document that match the selector.
dojo.query("a");
Running that in a console, you’ll get a NodeList
with 5 items. It holds the five anchor tags you’ll expect. What do you expect to get when you try dojo.query("p > a")
? dojo.query
can also take a root, or context element as a second parameter. As you might expect, this limits the scope of the query to elements inside that root element. So:
dojo.query("a", "nav"); // returns a `NodeList` of 4 <a>s
The root parameter can be either a DOM element, or a string that’s an ID of an element.
The returned NodeList
s also have a query
method, which finds nodes that match the selector that are children of the nodes in the original NodeList
. For example:
dojo.query("a"); // a `NodeList` of 5 <a>s dojo.query("p").query("a"); // a `NodeList` of 1 <a>
But wait, there’s more, as they say. There are two other Dojo methods for getting elements. If the element you want has an id
attribute, you can use the dojo.byId
method.
dojo.byId("nav");
If you try that out, you’ll notice that you don’t get a NodeList
object back: it’s just a plain old DOM element. This will be important to remember.
One more, and that’s even more specific: dojo.body()
. It returns the element, predictably.
Now, if there’s one “main thing” that most devs use their JS libraries for, it’s working with DOM elements. Of course, Dojo has all the facilities for this too, so let’s take the tour.
Creating Elements
We’ll start by creating elements, with dojo.create
. First, you can simply just get a new DOM element like this:
var h = dojo.create("h2"); // <h2></h2>
Simple. But, usually, you want to do more. Well, you can pass an attributes object as a second parameter:
var h = dojo.create("section", { role: "banner", innerHTML: "Learning Dojo"}); // <section> role="banner">Learning Dojo</section>
The dojo.create
method can also add elements directly to the DOM. For that, we can add parameters 3 and 4:
dojo.create("p", { innerHTML: "Hi there!"}, dojo.body(), "first"); dojo.create("h1", { innerHTML: "Heading"}, dojo.query("h1")[0], "before");
The third parameter is called the reference node; our new node will be placed in the DOM relative to that element.
But where, in reference?
That’s where the fourth parameters, the position, comes in. By default (i.e., if you leave it out), it is “last”, which appends the new element to the reference node (as its last child). Your other options are these:
- “first” prepends the new node to the reference node.
- “before” and “after” put the new node before or after the reference node.
- “replace” replaces the reference node with the new node.
- “only” replaces all the child elements of the reference node with the new node.
Modifying Nodes
You don’t know it yet, but you’ve pretty much learned the dojo.attr
method. Let’s formalize this introduction.
dojo.attr
is used to get and set attributes on DOM nodes. Remember that attributes object that we passed as the second parameter to dojo.create
? You can pass that as the second parameter to dojo.attr
. The first parameter, of course, is the node that is having its attributes modified (or an id string):
var navUl = dojo.query("p")[0]; dojo.attr(navUl, { onclick : function () { alert("Learning Dojo!"); }, role: "banner", style : { backgroundColor: "red", fontSize: "2em" } });
If you just want to set a single attribute, just pass the name as the second parameter and the value as the third:
dojo.attr("nav", "className", "module"); // first parameter is an id string
To get an attribute, only two parameters are required:
dojo.attr(dojo.byId("nav"), "id"); // "nav"
You can use the NodeList
method attr
in the same way:
var items = dojo.query("li"); items.attr("innerHTML"); // [" <a href="/">Home</a>", " <a href="/portfolio">Portfolio</a>", " <a href="/about">About</a>", " <a href="/contact">Contact</a>"] items.attr({ className: "btn" });
One more thing: to remove attributes, you can use dojo.removeAttr
and the NodeList
counterpart to remove attributes from elements completely:
dojo.removeAttr("nav", "id"); dojo.query("#nav").removeAttr("id");
There are other ways to modify those nodes, though. How about dojo.addClass
, dojo.removeClass
, or dojo.toggleClass
? You can use these to add, remove, or toggle a class or array of classes on single nodes:
var nav = dojo.byId("nav"); dojo.addClass(nav, "selected");
There are also NodeList
counterparts for these methods:
dojo.query("li").removeClass(["selected", "highlighted"]);
Oh, and don’t forget about dojo.replaceClass
and the NodeList
version:
dojo.query("p").replaceClass("newClass", "oldClass");
Removing Nodes
Want to get rid of a node? Easy: pass dojo.destroy
either a DOM node or an id string:
var navList = dojo.byId("nav"); dojo.destroy(navList); // or, easier: dojo.destroy("nav");
I should note that there’s no way to destroy a NodeList
; dojo.destroy
only accepts single nodes, and doesn’t have a NodeList
counterpart method.
But let’s say you just want to take nodes out of the DOM, but not actually destroy them. After all, you might want to plug them in somewhere else, or when something else happens. This is where the orphan
method comes in. This method is only a NodeList
method:
dojo.query("li").orphan();
On our example page, this removes the four
s and returns a NodeList
of them. If you’d only like to orphan
certain nodes from original NodeList
, pass is a filtering selector. Note that this filter only matches against nodes in the original NodeList
, and not their children:
dojo.query("li").orphan("li:first-of-type"); // will only orphan the first &lt;li>
While it isn’t removing an element, I’ll throw this in here: dojo.empty()
will take a single node or id and remove everything inside it. Behind the scenes, Dojo is actually just doing node.innerHTML = ""
. There’s also a NodeList
version of this method that, obviously, requires no parameters.
Moving / Duplicating Nodes
There are a couple methods related to moving or duplicating DOM nodes.
You’ll find you’re already partly familiar with dojo.place
, from dojo.create
. It takes three parameters: the node, the reference node, and the position. As you might expect, these parameters play the same roles they do in dojo.create
:
var nav = dojo.byId("nav"), p = dojo.query("p")[0]; dojo.place(nav, p, "after"); // moves `nav` to right after `p` in the DOM
Following the trend of so many Dojo DOM method, there’s a NodeList
method counterpart:
dojo.query("p").place(dojo.body(), "first");
Then there’s dojo.clone
. While it will clone more than just DOM node structures, that’s what we’ll use it for right now: if you pass this method a reference to a DOM node, it will clone, or copy, that node and all its children. This’ll duplicate our example navigation ul
, and put the copy at the top of the document:
var u2 = dojo.clone( dojo.byId("nav") ); dojo.attr(u2, "id", "nav2"); dojo.place(u2, dojo.body(), "first");
You can use dojo.clone
to clone other JavaScript objects as well.
var o1 = { one: "one"}, o2 = dojo.clone(o1); o1 === o2; // false
Then, there’s the NodeList
method adopt
. I’ll have to admit that, while this is an interesting method, I’m still not exactly sure where I would use it. Here’s what it does: it takes two parameters: a selector string or DOM node(s), and an optional position value, which has the same options as dojo.place
(“last” as default, etc. ). Then, the adopt
method will take the element(s) you passed in as the first parameter (or the elements in the DOM that match the selector) and position them relative to the first element in the NodeList
. Then, it returns the adopted elements as a new NodeList
. So, in our example page, this will replace all the children of the first <li> with the paragraph:
dojo.query("li").adopt("p", "only");
So, there’s that.
Iterating over Nodes
Since NodeList
s are similar to arrays, you could use just a regular for
loop to iterate over them. However, NodeList
s have a forEach
method:
dojo.query("li").forEach(function (element, index, arr) { // do your thing });
As you can see, the callback function takes three parameters, the element, the index, and the array itself. If you want to loop over other arrays, you can use dojo.forEach
in the same way, just passing that array as the first parameter:
dojo.forEach([1,2,3], function (item) { // act here });
forEach
returns the NodeList
or array that you started with. If you want to return a changed array, you can use the map
method. Whatever you return from the callback function will be in the array (or NodeList
) returned at the end.
dojo.map([1,2,3], function (item) { return item * item; }); // [1, 4, 9]
Somewhat related to this is filtering nodes out of a NodeList
, with filter
.You can just pass this method a CSS selector, and only elements that match it will be kept.
dojo.query("li").filter(".highlight"); // NodeList with one <li class="selected">
However, filter
can also take a callback function that receives three parameters: the current item, its index, and the array. If the function returns true
, the element is kept; otherwise, it’s left out. A new NodeList
of the kept elements is returned.
dojo.query("li").filter(function (el) { return dojo.query("a", el)[0].innerHTML === "About"; }); // returns a NodeList that holds only the list item with the text "About"
Handily, there’s also a dojo.filter
version that takes an array as the first parameter and the callback as a second.
dojo.filter(["Nettuts", "Psdtuts", "Phototuts"], function (el, idx, arr) { return el.slice(0,1) === "P" }); // ["Psdtuts", "Phototuts"]
Working with Events
Let’s now talk about events with Dojo. And we will start with DOM events, since that’s usually what you’re using. let’s say we want to do something when our <h1>
is clicked. There are several ways to do this, and we’ll discuss them all here.
First, let’s assume we are handling an event that occurs on an element or elements that we have retrieved with dojo.query
. We could use the onclick
method that NodeList
s have:
dojo.query("h1").onclick(function () { alert("Learning Dojo"); });
However, this is really just a “syntactic sugar” method. Behind the scenes, Dojo is using the connect
NodeList method:
dojo.query("h1").connect("onclick", function (e) { alert("learning Dojo"); });
This method actually passes the job on to another method, dojo.connect
; you’ll probably use this method directly when you’ve got a single DOM node that you want to handle an event on:
var h = dojo.query("h1")[0]; // or dojo.byId("some_element"), for example dojo.connect(h, "onclick", function () { alert("learning Dojo"); });
Notice how, each time we “move up a layer”, we add another parameter to the beginning of the method call.
Let’s talk briefly about disconnecting events. When using the methods provided on a NodeList
instance, there’s currently no easy way to disconnect the events. This is because dojo.connect
returns a handle that is used in the disconnection of events. To disconnect an event, pass its handle to dojo.disconnect
:
var h = dojo.query("h1")[0], handle = dojo.connect(h, "onclick", function () { alert("learning Dojo"); dojo.disconnect(handle); });
If you put that in your console, and then click the <h1>
, you’ll get an alert. Then the handler will be disconnected, so subsequent clicks won’t do anything.
If you want to create your own events (or, using Dojo’s terminology, your own topics), you can use Dojo’s publish
and subscribe
methods. If you’re familiar with how other pub/sub systems work, you’ll have no trouble with this.
To subscribe an topic, simply pass the name of the topic and the function that should run when the topic is published:
dojo.subscribe("myTopic", function (data, moreData) { alert(data); console.log(moreData); });
Then, you can publish the topic almost as easily:
dojo.publish("myTopic", ["some data", "some more data"]);
Notice that any data you want to pass to functions that subscribe to your topic gets put in an array and passed as a second parameter.
Conclusion
In this tutorial, we’ve covered probably 90% of the DOM functionality built into the Dojo Base file we’re getting from Google’s CDN. However, there’s more functionality to discuss. Before we get there, however, we’re going to have to learn about pulling in Dojo’s extra functionality. We’ll discuss that and much more in the next episode of this series.
Have any requests for things you’d like to learn to do in Dojo? I always read the comments, so you know what to do!
Comments