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.
In this second episode, we’ll be discussing loading modules and using some of the DOM-enhancing modules.
Prefer a Video?
Remember, for premium members, there’s a screencast available. Besides covering everything in this tutorial, I also cover Dojo’s AJAX and cross-domain AJAX methods. So, sign in to get these screencasts and all the other incredible Tuts+ Premium screencasts, tutorials, and resources.
You’ll recall that there’s a lot more to Dojo than comes in the Dojo Base file that we loaded from the Google CDN in episode 1. There’s a whole lot more, in fact. So, how do we harness this whole lot more?
Well, the rest of Dojo is available in modules. We can load in a module to get its functionality. Behind the scenes, every module is a JavaScript file of its own. Basically, The string you use you refer to the module is its path name (minus the “.js”).
To get a better idea of how this works, download the Dojo Toolkit release. After you unzip this rather hefty file, you’ll see this:
To load
able/baker/charlie.js
, we use'able.baker.charlie'
. Couldn’t be easier, right?
Notice, there’s a folder for each of the three members of the Dojo trinity. By default, this folder (here, called “dojo-release-1.6.1
”) is the root for all our module paths.
Take a look inside the “dojo
” folder. You’ll see the file dojo.js
, which is the minified Dojo base file that we’ve been loading from Google. Notice further down, there’s a file called NodeList-traverse.js
. If we wanted to use the functionality that file provides (and we will, in this tutorial), we would get it using the module path 'dojo.NodeList-traverse'
(yes, I know you don’t know where we’ll use this string yet, but stick with me). Now, see that folder called io
? If we wanted to load the script.js
file in there, we’d use 'dojo.script.io'
. Getting the hang of this? Basically, to load able/baker/charlie.js
, we use 'able.baker.charlie'
. Couldn’t be easier, right?
So, where are we using these strings, exactly? We can pass these module path strings to the dojo.require
function. This will load that file in, via an XHR request. Once that file loads, you’ll be able to use whatever it brought to the table.
I should note that you don’t want to try to use the pieces you’re loading before they’re actually loaded. To avoid this, you should use dojo.ready
, which is just an alias for dojo.addOnLoad
. Any function you pass to it will run once the code is loaded. It’s a lot like jQuery’s $(document).ready()
function. In this tutorial, you can use your browser’s JavaScript console to try out all these snippets, so we won’t actually have a dojo.ready
call here.
So, let’s start looking at some modules. I’ll note here that we won’t necessarily be looking at all the methods each of these modules has to offer; we’ll look at ones you’ll find most useful as you’re learning Dojo.
Oh, one more thing: if you’re following along, you can use this HTML to get the same results as I show:
<html> <head> <title> Dig into Dojo, part 2 </title> <style> body { font: 16px/1.5 helvetica; color: #474747; } #test { border: 1px solid #ccc; background: #ececec; width: 250; padding: 20px; text-align: center; } .active { background: maroon; } </style> </head> <body> <div id='content'> <h1> Dig into Dojo Episode 2</h1> <p id='test'> Dojo Tutorial </p> <div> An Unsemantic Div </div> <ul> <li> A Child! 1 </li> <li> A Child! 2 </li> <li> A Child! 3 </li> <li> A Child! 4 </li> </ul> <p> Another Paragraph </p> </div> <script src='http://ajax.googleapis.com/ajax/libs/dojo/1.6.1/dojo/dojo.xd.js.uncompressed.js'></script> <script> </script> </body> </html>
dojo.require('dojo.NodeList-data');
Let’s start with a relatively simple one: the NodeList
data methods. To load them:
dojo.require('dojo.NodeList-data');
To cement the idea of these paths, go look for the file you just loaded. You should find it in [dojo-download]/dojo/NodeList-data.js
. Once you’re a little more familiar with Dojo, I’d recommend looking through a few modules to see how they’re built.
Note: Even though we downloaded Dojo, we’re still using the Google CDN version, which means any files we require are being loaded from there. If you want to use your local copy, you’ll have to fire up Apache (or otherwise), because the files are loaded via XHR.
So, dojo.NodeList-data
adds two NodeList
methods: data
, and removeData
. Let’s see how this works:
var ps = dojo.query('p'); ps.data('key', 'value'); ps.data('key'); // ['value', 'value']
Setting and getting data is a pretty straightforward process: pass a key (always a string) and a value (any JavaScript type) to set the data, and just pass the key to get it back. You’ll notice that when getting, data
returns an array. This is because NodeList
s are arrays, and may have more than one node. Therefore, we’re returning the value for every node. data
will always return an array, even if there is only one item in the NodeList
.
If you pass data
no parameters, it will return an array with an object for each node in the NodeList
: each object will have the appropriate keys and values. See here:
dojo.query('#test').data('handle', function () { /* action! */ }); dojo.query('p').at(0).data('name', 'Dojo Toolkit'); dojo.query('p').at(1).data('age', 1.6); dojo.query('p').data; // returns: [ { handle: function () { /* action! */}, name: 'Dojo Toolkit' }, { age: 1.6 }]
Now, about removeData
: pass it a key and it will remove that key and value from every node. Pass it no parameters, and it removes everything:
// assuming the above: var ps = dojo.query('p'); ps.removeData(); ps.data(); // [ {}, {} ]
Of course, data does not persist across page refreshes.
dojo.require('NodeList-fx');
If you’re familiar with jQuery’s
fadeIn
andfadeOut
methods, you’ll think you know these … and you’ll be wrong.
Next up is a collection of animation methods. Animation with JavaScript functions can be somewhat challenging, so we won’t be covering animation very much at all in this series, but we’ll look at a couple basic methods here.
I should note that this module primarily provides NodeList
methods to many of the dojo.*
methods in the dojo.fx
module. We won’t be discussing those specifically, but much of this applies to those methods as well.
First, there are the mandatory fadeIn
and fadeOut
. If you’re familiar with jQuery’s fadeIn
and fadeOut
methods, you’ll think you know these … and you’ll be wrong. There are some interesting differences we should cover.
These methods—and the others in this module—are actually wrapper methods for some of Dojo’s incredibly complex animation functionality. The first difference is that calling one of these methods doesn’t automatically run the animation; it returns a dojo.Animation
object. Then, to run the animation, you call the dojo.Animation
object’s play
method. Alternately, you can pass auto: true
as a key/value in the options object you might pass to the animation method.
Other options you can pass to an animation method include delay
, duration
, and easing
, among others. You can also include functions that run before or after certain events: beforeBegin
, onBegin
, onEnd
, onPlay
, and onAnimate
.
A good example of all this behaviour is making the Dojo fadeOut
function work like the jQuery version. Here’s what we’d do to make our first paragraph fade out over 2 seconds, with each library:
jQuery('#test').fadeOut(2000); dojo.query('#test').fadeOut({ auto: true, duration: 2000, onEnd: function (node) { dojo.style(node, 'display', 'none'); } }); // alternately: dojo.query('#test').fadeOut({ duration: 2000, onEnd: function (node) { dojo.style(node, 'display', 'none'); } }).play();
All this goes for the fadeIn
, wipeOut
, and wipeIn
effects as well.
Then, there’s the animateProperty
method. This is a really handy way to change the CSS properties of an object, animating them while doing it. You’ll pass an options object to that method. Besides taking all the properties that fadeOut
and friends take (delay
, duration
, onEnd
, etc.), this method takes a properties
property (how meta!), with your CSS values. Of course, this returns a dojo.Animation
object, so you pretty much call the play
method, or use the auto: true
property.
Here are a couple of examples:
This line will fade the background colour of our element to red over 2 seconds:
dojo.query('#test').animateProperty({ duration: 2000, properties: { backgroundColor: 'red' } }).play();
This line will wait 4 seconds, and then widen our element to 1200px, and alert “done” when finished. Notice that first we load the dojo.fx.easing
module first. This is just a set of easing functions that you can use as I have below wherever easing is accepted.
dojo.require('dojo.fx.easing'); dojo.query('#test').animateProperty({ delay: 4000, properties: { width: 1200 }, easing: dojo.fx.easing.bounceOut, onEnd: function () { alert('done'); }, auto: true });
One more. This shows a more advanced use of the CSS properties: instead of just putting where it should end, you can also define where the property should start. Of course, if that’s not the current value of the property, it won’t be animated to the starting point.
dojo.query('#test').animateProperty({ properties: { fontSize: { start: 20, end: 120 }, backgroundColor: { start: 'red', end: 'blue' }, width: { start: 100, end: 1200 } }, easing: dojo.fx.easing.quintIn, duration: 10000 }).play();
Of course, animation is a hydra that we could spend a long time slaying, so we’ll leave it at that.
dojo.require('dojo.NodeList-manipulate');
We discussed several methods for manipulating DOM elements in episode 1 of this series, but loading the dojo.NodeList-manipulate
module gives us a few more methods to work with. Let’s check ‘em out.
First up, there’s the innerHTML
and text
methods. They do very much what you’d expect: set the HTML or text within the element to whatever you pass in.
// <p id='test'> Dojo Tutorial </p> dojo.query('#test').innerHTML('<strong> Unicorns! </strong>'); // <p id='test'><strong> Unicorns! </strong></p> // <p id='test'> Dojo Tutorial </p> dojo.query('#test').text('<strong> Unicorns! </strong>'); // <p id='test'>&lt;strong> Unicorns! &lt;/strong></p>
You can also pass actual DOM nodes to innerHTML
.
Next, a warm round of applause for append
and prepend
, and their cousins appendTo
and prependTo
. These are very similar to innerHTML
, except they don’t get rid of what is currently in the node(s) in question.
// <p id='test'> Dojo Tutorial </p> dojo.query('#test').append('<strong> Unicorns! </strong>'); // <p id='test'> Dojo Tutorial <strong> Unicorns! </strong></p> // <p id='test'> Dojo Tutorial </p> dojo.query('#test').prepend(dojo.query('p').at(1)); // <p id='test'><p>Another Paragraph</p> Dojo Tutorial </p>
When you’re moving DOM nodes, as in that last example, it might be easier to start with the node you want to move. Then, you can use appendTo
or prependTo
, passing in a selector, to tell Dojo where to put the node:
dojo.query('h1').appendTo('p');
This will append the <h1>
on our example page to the two paragraphs on the page. Notice that the &h1>
will be removed from its original location, and cloned for each paragraph.
Our next act is after
, before
, insertAfter
, and insertBefore
. You’ll find these quite similar to the append
, et al methods; it’s just that they put the content before or after the element in question. A code snippet is worth a thousand words, here:
// <p id='test'> Dojo Tutorial </p> dojo.query('#test').after('<em> NETTUTS+ </em>'); // <p id='test'> Dojo Tutorial </p><em> NETTUTS+ </em> // <p> Another Paragraph </p> dojo.query('h1').insertBefore(dojo.query('p:last-of-type')); // <h1> Intro to Dojo Episode 2</h1> <p> Another Paragraph </p>
I think you’re starting to get it.
Now, the NodeList
methods wrap
, wrapAll
, and wrapInner
. These are pretty straightforward:
wrap
will wrap the items in the NodeList
with the HTML string you pass in:
dojo.query('p').wrap('<strong>'); // all paragraphs will be wrapped in <strong>, as unsemantic as that is
wrapAll
will move all the nodes in your NodeList
to where the first one is, and wrap them all in the HTML string you pass in:
dojo.query('p').wrapApp('<div class='active'></div>'); // if you add the closing tag, Dojo will figure out what you mean
wrapInner
will wrap everything inside the nodes in your NodeList
, essentially making it the only child node of each node:
dojo.query('body').wrapInner('<div id='main'>');
dojo.require('dojo.NodeList-traverse');
We saw a very few select methods for moving around in episode 1, but—wait for it‐there’s more if you dojo.require('dojo.NodeList-traverse')
.
I know you’re excited to see these methods, but a note first: all these NodeList
methods change the NodeList
in some way. To get back to the original NodeList
, use the end
method, just like jQuery.
children
: Captures the child nodes of the nodes in your NodeList
; optionally, takes a selector to filter those kids:
dojo.query('ul').children(); // four <li>s
parent
: Captures the parent node of the nodes in your NodeList
. There’s also a parents
(plural) method that returns the parent, grandparent, etc. Both take filtering selectors.
dojo.query('li').parent(); // [<ul>] dojo.query('li').parents(); // [<ul>, <div id='content'>, <body>, <html>]
siblings
: Gets the siblings of each node. Can be filtered.
dojo.query('#test').siblings(); // [<h1>, <div>, <ul>, <p>]
next
, prev
, nextAll
, and prevail
: These methods are sub-methods, if you will, of siblings
: they all return a certain group of siblings. next
returns the sibling node after each node in the NodeList
; prev
returns the one before each node. nextAll
and prevAll
return all the next or previous siblings of each node in the NodeList
.
dojo.query('#test').next(); // [<div>] dojo.query('p').prev(); // [ <h1>, <ul> ] dojo.query('li').prevAll(); [<li> A Child! 1 </li>, <li> A Child! 2 </li>, <li> A Child! 3 </li>]
first
/ last
: Very simply, these methods return the first and last methods of the NodeList
:
dojo.query('li').first(); //[<li> A Child! 1 </li>] dojo.query('li').last(); //[<li> A Child! 4 </li>]
even
/ odd
: Get your odd or even nodes from the NodeList
here:
dojo.query('li').even(); // [<li> A Child! 2 </li>, <li> A Child! 4 </li> ] dojo.query('li').odd(); // [<li> A Child! 1 </li>, <li> A Child! 3 </li> ]
And there you have it: four helpful modules for working with the DOM. Of course, there are dozens of dozens of other modules available, many more than I could cover in an introductory series. I encourage you to dig around in that dojo
directory in the toolkit download and see what you can find.
Conclusion
Now that you’re familiar with a couple of Dojo modules, I’d recommend you poke around inside the modules themselves to see how they add their functionality. While I won’t be able to explain exactly how modules are created in this series, what you see in there might be a good jumping-off point for some of your own research.
Thanks for sticking around, and keep your eyes out for Episode 3, where we’ll take a look at Dijit.
Comments