There are a number of modules in dojo for UI related tasks
Dojo provides three modules with basic functions on the document tree:
All of the functions above are written to either mask browser incompatibilites, or to provide functionality that wasn't provided within the standard javascript API.
All of the functions can take either a node or an id as their first argument.
One particular set of functions to note are the sizing functions.
An HTML block element has the following format:
+-------------------------+
| margin |
| +---------------------+ |
| | border | |
| | +-----------------+ | |
| | | padding | | |
| | | +-------------+ | | |
| | | | content | | | |
| | | +-------------+ | | |
| | +-|-------------|-+ | |
| +-|-|-------------|-|-+ |
+-|-|-|-------------|-|-|-+
| | | | | | | |
| | | |<- content ->| | | |
| |<------ inner ------>| |
|<-------- outer -------->|
+-------------------------+
There are three sizes associated with this element:
Depending on browser, and mode, asking for the width/height of an element will return different results; sometimes it will give you the content size and sometimes it will give you the inner size. Therefore, it's important to always use dojo's functions for getting/setting the size;
An HTML element can have multiple classes, such as:
Dojo provides functions to handle this class string as an ordered set, so you don't need to do string manipulations to add/remove items from the class list:
There are many more DOM related functions. Please see the reference doc for details.
- dojo.html.addClass(node, className)
- dojo.html.prependClass(node, className)
- dojo.html.removeClass(node, className)
- dojo.html.replaceClass(node, className, oldClassName)
Drag and Drop (DnD)
When Microsoft and Netscape introduced Dynamic HTML (different versions of course), the drag-and-drop demos elicited much applause. You could put a shopping cart on the left and catalog items on the right, and the customer could drag items right to the shopping cart. Neato! It looked just like a client-server application.
Because of the incompabilities between DHMTL, writing a cross-platform drag-and-drop application was difficult. Dojo makes it easy by layering an easy-to-use API over the top.
To drag and drop, you need three things:
- Something to drag, called a DragSource. In Dojo, any HTML element can be one.
- Some place to drop, called a DropTarget. This too can be any HTML element.
- Something to do when the item is dropped
A Simple Example
(Under construction)
Here's a simple application of drag and drop to simulate a kitchen. You have three drop targets - Frying Pan, Pot of Boiling Water, and Oven - and two drag sources - Egg and Chicken Leg. A user can drag the word Egg to the box surrounding Pot of Boiling Water to simulate boiling the egg. You can drag the foods from their initial locations, or from one destination to another, as in dragging the Egg from the Pot of Boiling Water to the Frying Pan.
<script type="text/javascript' src="/path/to/dojo.js"></script> <script type='text/javascript"> dojo.require("dojo.dnd.*"); dojo.require("dojo.event.*"); function initKtichen() { // "pan" matches the id for the <div> tag with id="pan" below // The [] around "dest" are required, for reasons we'll see later new dojo.dnd.HtmlDropTarget(dojo.byId("pan"), ["dest"]); new dojo.dnd.HtmlDropTarget(dojo.byId("pot"), ["dest"]); new dojo.dnd.HtmlDropTarget(dojo.byId("oven"), ["dest"]); // "dest" matches the "dest" in the DropTarget's above. new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest"); new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest"); } dojo.addOnLoad("initKitchen"); </script> </head> <body> <H1>Food</H1> <div id="egg">Egg</div> <div id="leg">ChickenLeg</div> <H1>Destinations</H1> // This is the pan, the first drop target <div id="pan" style="border:3px soid black;width:200px"> Frying Pan </div> <div id="pot" style="border:3px solid black;width:200px"> Pot of Boiling Water </div> <div id="oven" style="border:3px soid black;width:200px"> Oven </div> <br />(Vegetarians may substitute soy products with no loss of functionality) Running this example, you notice things happen automagically:
- The item moves with your mouse pointer as you drag
- When you drop the item on a drop target, the drop target expands to hold the item
- If you try to drop an item outside of a drop target, the item snaps back to its original position.
- A horizontal line appears where the drop will occur. In this example, you'll see the top border of the drop target become "fatter".
The HTMLDragSource and HTMLDropTarget constructor calls contain two arguments:
The example does a lot with a little bit of code. In the next examples, we'll work on making the user interface discoverable, meaning the user can figure it out with visual cues.
- The component do be dragged or dropped, respectively. dojo.byId() is helpful here, and an easier-to-type shortcut for document.getElementById().
- A destination code to specify which drag sources can do to which drop targets. We'll see that work in LimitingDragAndDropOptions.
Beautification
Let's put those events to good use. When a user drags an object, they'd like to know where it's legal to drop. We can help out by visually indicating a legal drop target. Going forward with our kitchen example, when the user drags a food item to a utensil, we'll make the utensil border red. When they drop, or drag out of that utensil, we'll make it black again.
To accomplish this, the natural events to use are onDragOver and onDragOut. These act a lot like the onMouseOver and onMouseOut attributes of an HTML tag.
function initKitchen(){
dojo.declare("dojo.book.dnd.DestDropTarget",dojo.dnd.HtmlDropTarget,{
onDragOver: function(e) {
// domNode is the drop target we're over
this.domNode.style.borderColor = "red";
dojo.dnd.HtmlDropTarget.prototype.onDragOver.apply(this, arguments);
},
onDragOut: function(e) {
// this.domNode is the drop target we're leaving
this.domNode.style.borderColor = "black";
dojo.dnd.HtmlDropTarget.prototype.onDragOut.apply(this, arguments);
}
});
new dojo.book.dnd.DestDropTarget(dojo.byId("pan"), ["dest"]);
new dojo.book.dnd.DestDropTarget(dojo.byId("pot"), ["dest"]);
new dojo.book.dnd.DestDropTarget(dojo.byId("oven"), ["dest"]);
new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest");
new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest");
}
Drag and Drop Actions
OK, we have drag sources and drop targets. But without an action, drag and drop isn't very interesting. We want something to happen when the item is dropped.
Because Drag and Drop uses Dojo's event model, you can set up actions with very few lines of code. (If you haven't reviewed the Object Oriented Concepts section, now's a good time.) Here's a simple example, which displays an alert box when the item is dropped.
function initKitchen(){ dojo.declare(dojo.book.dnd.DestDropTarget",dojo.dnd.HtmlDropTarget,{ onDrop: function(e) { alert('Ready to cook!'); // Call the superclass method to do the actual dropping dojo.dnd.HtmlDropTarget.prototype.onDrop.apply(this, arguments); } }); new dojo.book.dnd.DestDropTarget(dojo.byId("pan"), ["dest"); new dojo.book.dnd.DestDropTarget(dojo.byId("pot"), ["dest"]); new dojo.book.dnd.DestDropTarget(dojo.byId("oven", ["dest"]); new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest"); new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest"); }Notice how the drop target object type is no longer HtmlDropTarget, but your new class DestDropTarget. This is simple inheritance. A DestDropTarget is a more specific kind of HtmlDropTarget. Most of the methods are delegated to the superclass, HtmlDropTarget, but our specific functionality for onDrop is added before the superclass call.
Drop Events
onDrop is the most common event to override for drop targets. But there are others. To connect an action to one of these events, simply specify the method for that event in your dojo.declare call. All events without an action will be handled by HtmlDropTarget.
- onDragOver(e) - called when the user begins dragging a source over this drop target. It is called only once when the drag source "flies over".
- onDragMove(e) - called repeatedly as the drag source is over the drop target. You shouldn't perform any long calculations here.
- onDragOut(e) - the opposite of onDragOver, this is called when the drag source leaves the drop target area without having been dropped. This is called only once.
So the events fire like this:
- For dragging across a drop target without dropping - onDragOver(), onDragMove(), onDragMove(), ... onDragMove(), onDragOut().
- For dragging into a drop target and dropping - onDragOver(), onDragMove(), onDragMove(), ... onDragMove(), onDrop().
Like all event handlers, your code must include one parameter for the event information itself. This object is of type dojo.dnd.DragEvent, because Drag events and Drop events have identical event information. DragEvent contains the following readable fields:
The onDropEvent also has onDropStart and onDropEnd events before and after, accordingly. onDropStart is a good place to verify the drop target is OK, although destinations do the job easier.
- dragObject - the object being dragged.
- dragSource - the dragSource being dragged. Note that dragSource.dragObject is the same as dragObject. (Confusing, but remember the HtmlDragSource constructor takes two parameters: the object and the possible destinations).
- target - the drop target for that drop. Null if the drag source hasn't been dropped yet.
Drag Events
Each of these events also has a DragEvent parameter with event information.
Note that not every drag ends with a drop! The user can drag a source, then leave the browser window entirely and let go of the mouse button. Nothing will be dropped, since a browser doesn't usually interact with other windows. But onDragEnd will be called nonetheless.
- onSelected(e) - called when a drag source is clicked
- onDragStart(e) - called once when dragging begins
- onDragEnd(e) - called once when dragging ends. For drops, this event is called right before onDrop.
Limiting Drag and Drop Options
Using default settings as we have, the user can drag items all over the page. They may have to try several potential drop before stumbling on the right one. You can help them out by limiting their choices.
Disabling Drop Targets for Certain Sources
Continuing from our example, suppose that chicken legs can go into all destinations: the oven, the pot, or the frying pan. But eggs can go only into the pot or pan, not in the oven. (Yes there is such a thing as baked eggs, but you're not Wolfgang Puck!)
That's where the "dest" parameter comes in. The second argument for both HtmlDragSource and HtmlDropTarget, this acts much like the name="..." parameter of radio buttons. They tie different drop sources with a common set of rules.
In the following example, we set up two destination groups: allCookingOptions, and topOfStoveOnly. The HTML portion is exactly like our first example, but we change initKitchen to:
function initKitchen(){
// A pan can be a drop target for any drag source of type topOfStoveOnly or
// allCookingOptions.
new dojo.dnd.HtmlDropTarget(dojo.byId("pan"), ["allCookingOptions","topOfStoveOnly"]);
new dojo.dnd.HtmlDropTarget(dojo.byId("pot"), ["allCookingOptions","topOfStoveOnly"]);
new dojo.dnd.HtmlDropTarget(dojo.byId("oven"), ["allCookingOptions"]);
// An egg can only be dropped on a target of type topOfStoveOnly
new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "topOfStoveOnly");
new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "allCookingOptions");
}You may wonder, "Why the brackets around destinations in a drop target?" Drag sources belong to one and only one group, but drop targets can belong to many groups. That's why drop targets always have an array of destinations signified by [...], even when there's only one group.
Constraining The Drag Area
To make the User Interface more discoverable, you can put a boundary around all the possible drop targets.  This prevents the drag source from leaving the area, even if the mouse is dragged outside. The technique is especially helpful if you have two or more sets of drag and drop areas. For example, if you had a drop area of domestic cities and a drop area of international cities, you could constrain domestic planes (our drag source) to the domestic cities.ÂÂ
To constrain the drag sources, you use the constrain() method. That requires first constructing the HtmlDragSource object, as we have in previous examples. Then you call constrain with the id of the bounding box. Note this can be any HTML object, and the boundaries of that object act as the constraining box. Butis a natural choice for a bounding box.ÂÂExtending our kitchen example, the user will not be able to drag the egg or chicken leg outside an imaginary box containing all three destinations.
function initKitchen(){ ... // Construct drop sources // Keep the egg from leaving the boundaries of the object kitchenDiv var eggSource = new dojo.dnd.HtmlDragSource(dojo.byId("egg"), "dest"); eggSource.constrainTo("kitchenDiv"); // Do the same with the chicken leg legSource = new dojo.dnd.HtmlDragSource(dojo.byId("leg"), "dest"); legSource.constrainTo("kitchenDiv"); } ... <H1>Destinations </H1> <div id="kitchenDiv"> <div id="pan" ...The user cannot drag either the egg or the chicken leg outside of the bounding box, although they can try to drop it (unsuccessfully) between the destinations. Later we'll see how to give more visual cues to the user as to where they can drop.
List Rearrangement
Yes, you can use Drag and Drop outside of the kitchen! One popular application is rearranging lists. You can do this by enabling list items as drag sources and the list itself as a drop target. That seems kind of wierd at first - you do not expect drag sources and drop targets to overlap, much less contain one another. Here, it helps to think of dragging and dropping as a shortcut for cut-and-paste.
In this example you can drag the elements, Jim Hendrix albums, into whatever order you wish.
<html><head> <script type="text/javascript" src="/path/to/dojo.js"></script> <script type="text/javascript"> dojo.require("dojo.dnd.*"); dojo.require("dojo.dnd.event.*"); function initList() { // Loop through all li elements of list, and make them drop targets var dl = dojo.byId("listToRearrange"); var lis = dl.getElementsByTagName("li"); for (var i=0; i<lis.length; i++) new dojo.dnd.HtmlDragSource(lis[i], "dest"); new dojo.dnd.HtmlDropTarget(dl,"dest"); } dojo.addOnLoad(initList); </script> </head> <body> <H1>Jimi Hendrix Albums</H1> <p>Arrange in order of your own preference.</p> <ul id="listToRearrange"> <li>Electric Ladyland</li> <li>Are You Experienced?</li> <li>Axis, Bold as Love</li> </ul> </body> </html>Once this is done, you can use getElementsByTagName to loop through the elements in their new order, then send them by dojo.io or a page submission.
LFX
The dojo.lfx.* module is dojo's animation system. It includes many “canned� effects:In addition, it has a powerful system for chaining together primitives:
- fadeIn, fadeShow, fadeOut, fadeHide,
- wipeIn, wipeOut
- slideTo
- explode, implode
- highlight, unhighlight
// wipe two elements out, one after
// the other, following a 300ms delayvar anim1 = dojo.lfx.wipeOut(�foo�, 300);
var anim2 = dojo.lfx.wipeOut(�bar�, 500);
var composed = dojo.lfx.chain(anim1, anim2);
composed.play(300);// fade out three nodes together, using
// accelerationdojo.lfx.fadeOut(
[�foo�, “bar�, “baz�],
300,
dojo.lfx.easeInOut
).play();