This text is obsolete in parts. Please refer to the official DnD documentation.
Drag and Drop (DnD) in web applications is one thing which is gaining popularity these days. Already there is a handful of sites using drag and drop functionality coupled with AJAX to deliver some killer content. Suppose, one day your boss sees one such cool website . You are ordered to implement similar thing in the web application you are working on. You ask yourself if there is a solution that is easy, fast, tested, reusable, customizable and works with all major browsers !
The Dojo DnD API enables you to do all the cool(and complex) drag and drop stuff in a clean way with less pain.
This text is obsolete in parts. Please refer to the official DnD documentation.
Source: A source is an HTML node that can be dragged around. It can be a simple node, or a node which contains other nodes - a DIV, a TABLE, etc. In Dojo, a source has type dojo.dnd.Source.
Target: Target is like a placeholder, onto which nodes can be dropped. A good example is a shopping cart application, where an item icon is a source while the shopping cart is a target.
Interestingly, all dojo.dnd.Source objects in Dojo DnD are also targets, so many applications just use dojo.dnd.Source for both. For a pure target, i.e. one that's not draggable, you define the target with type dojo.dnd.Target.
Avatar: Avatar is kind of ghost image of the object being dragged. This ghost image follows the mouse cursor, till the time it is dropped to a proper placeholder. In Dojo this functionality is available to every drag and drop operation by default. The 'Dojo.dnd.Avatar' class does all this for you and displays a nice tooltop with text 'moving' or 'copying', indicating the type of operation being performed.
DnD Manager: The DnD Manager is a singleton, which is responsible for handling all DnD operations on a page. It accomplishes many complex tasks required for successful DnD operations like detecting initialization of valid drag operation by user, creation of Avatar, allowing drop to only eligible targets and such things. Most of the time the default manager works fine, but you can extend or create your own DnD Manager class for more complex scenarios.
Mover: Mover is a mixin class which makes an object (HTML node) movable. Once a node is made movable using this class, user can freely drag and drop the node anywhere on the page, for example a popup window. There is no concept of source and target in this case.
Selector : Selector is the base class for Source in Dojo. This means that every Source is a Selector. A Selector controls how its child nodes can be selected. This also means that every node that participates in DnD operation is part of some selectable list.
Container: Container is the baseclass for Selector. It provides functionality of sensing mouse movement over its children.
To sum up: a container contains nodes, a selector makes it possible to select single or multiple nodes, a mover makes the selected nodes movable, an Avatar provides visual cue during DnD and the DnD Manager makes the overall operation possible.
This text is obsolete in parts. Please refer to the official DnD documentation.
Let's make things interesting: a simple example of Dojo DnD in action ! In this example we will implement very basic DnD functionality in a web page. We will have a red and a blue rectangle that can be dragged and dropped onto a target.
First, we'll start with the standard header and some CSS:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}This is an important part. In this example we have only defined four classes. The 'target' and the 'source' class will be used by Dojo. The other two classes are used to create the red and blue rectangle.
Note: in 0.9, to use the class dojo.dnd.Source, you must dojo.require the resource dojo.dnd.source. Note the capitalization here. In Dojo 1.0+, both are capitalized.
Next, to hold the source and targets, we have a table with two cells. The first(left) cell hosts a source and the second(right) cell hosts a target:
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}The outermost <div> tag creates a source object. The inner <div> tags with class 'dojoDnDItem' create nodes, that can participate in DnD. The 'dndType' attribute can be used to specify 'type' of a node. You can specify more than one type for a node. Our nodes contains a red and blue rectangle with text on them. You can replace this content with whatever you like.
/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */ .geshifilter {font-family: monospace;} .geshifilter .imp {font-weight: bold; color: red;} .geshifilter .kw1 {color: #b1b100;} .geshifilter .kw2 {color: #000000; font-weight: bold;} .geshifilter .kw3 {color: #000066;} .geshifilter .coMULTI {color: #808080; font-style: italic;} .geshifilter .es0 {color: #000099; font-weight: bold;} .geshifilter .br0 {color: #66cc66;} .geshifilter .st0 {color: #ff0000;} .geshifilter .nu0 {color: #cc66cc;} .geshifilter .sc0 {color: #00bbdd;} .geshifilter .sc1 {color: #ddbb00;} .geshifilter .sc2 {color: #009900;}The above markup creates a pure target that can accept nodes with type 'blue' and 'darkred'. The accept attribute is a comma separated list of 'types' which can be dropped onto this node. This means that only those nodes whose 'dndType' is present in the list can be dropped onto this target. You can restrict DnD operations easily by specifying appropriate 'types' for nodes and targets. With no effort on your part, Dojo creates a default 'avatar' for each element when it is being dragged
As you can see, with pure markup and no code at all we added awesome drag and drop functionality to our page. This was a simple example of DnD, in the next chapters we will dive into more details of the API. We will see how to customize DnD behaviour, use events and restricting DnD operations.
This text is obsolete in parts. Please refer to the official DnD documentation.
Our last example looked pretty average. After playing with it for some time you may realize that visual indicators for DnD actions could be a great addition. Wouldn't it be nice if you could customize look of nodes so as to indicate important conditions or events in drag and drop functionality?
This is made very easy in Dojo. You don't have to intercept events and add your own code to modify the look of the nodes. Nodes that take part in DnD make use of certain predefined CSS classes. All you need is to supply a declaration to customize look and feel. We will making some changes to the last example to make it beautiful.
First, we will be using these two images in place of those ugly rectangles (BLUE.png and RED.png). [inline:BLUE.png=test] [inline:RED.png=test]
Then, we change our markup a bit. We will use <img> tags instead of <div>. Finally, we add the magic <style> section. Many classes have been defined in this section. You may want to play around with them a bit. A list of such class can be found in 'dndDefault.css' file located in dojotoolkit/dojo/tests/dnd directory. Given below is the complete source :
<html>
<head>
<title>Beautification</title>
<style type="text/css">
.target {border: 1px dotted gray; width: 300px; height: 300px;padding: 5px;
-moz-border-radius:8pt 8pt;radius:8pt;}
.source {border: 1px dotted skyblue;height: 200px; width: 300px;
-moz-border-radius:8pt 8pt;radius:8pt;}
.dojoDndItemOver {background: #feb;border: 1px dotted gray; }
.dojoDndItemBefore {border-left: 2px dotted gray; }
.dojoDndItemAfter {border-right: 2px dotted gray; }
.target .dojoDndItemAnchor {border:1px solid gray;}
.dojoDndAvatar {font-size: 75%; color: black;}
.dojoDndAvatar td {padding-left: 20px; padding-right: 4px;height:20px}
.dojoDndAvatarHeader {background: #ccc; background-repeat: no-repeat;}
.dojoDndAvatarItem {background: #eee;}
.dojoDndMove .dojoDndAvatarHeader {background-image: url(images/dndNoMove.png);}
.dojoDndMove .dojoDndAvatarCanDrop .dojoDndAvatarHeader {background-image:
url(images/dndMove.png);}
</style>
<script type="text/javascript" src="../../dojo.js" djConfig="parseOnLoad: true">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.source"); // capital-S Source in 1.0
dojo.require("dojo.parser");
</script>
</head>
<body style="font-size: 12px;">
<h1>A Beautification Example</h1>
<table><tbody><tr>
<td>
SOURCE
<div dojoType="dojo.dnd.Source" jsId="c1" class="source">
<img src="BLUE.png" class="dojoDndItem" dndType="blue"/>
<img src="RED.png" class="dojoDndItem" dndType="red"/>
<img src="BLUE.png" class="dojoDndItem" dndType="blue"/>
<img src="RED.png" class="dojoDndItem" dndType="red"/>
<img src="BLUE.png" class="dojoDndItem" dndType="blue"/>
<img src="RED.png" class="dojoDndItem" dndType="red"/>
<img src="BLUE.png" class="dojoDndItem" dndType="blue"/>
<img src="RED.png" class="dojoDndItem" dndType="red"/>
<img src="BLUE.png" class="dojoDndItem" dndType="blue"/>
</div>
</td>
<td>
TARGET
<div dojoType="dojo.dnd.Target" jsId="c2" class="target" accept="red,blue">
</div>
</td>
</tr><tbody/></table>
</body>
</html>
Note that we haven't added any code yet. But now if you run this example you will agree that it certainly looks more beautiful. Some points worth noticing are:
This text is obsolete in parts. Please refer to the official DnD documentation. Instead of complicated topic processors you should use onDrop(), onDropInternal(), onDropExternal() local events.
The following messages can be subscribed to :
dojo.subscribe("/dnd/drop", function(source,nodes,iscopy){
//code to perform some action
});
dojo.subscribe("/dnd/start", function(source,nodes,iscopy){
//code to perform some action
});
dojo.subscribe("/dnd/source/over", function(source){
//code to perform some action
});
dojo.subscribe("/dnd/cancel", function(){
//code to perform some action
});
The parameters to the callback function are:
var jsnode = source.getItem(nodes[0].id); var d = jsnode.data; var t = jsnode.type;
If you are interested in getting the target object, then you can use following method:
var t = dojo.dnd.manager().target;When we create a target or source via markup, we can specify a name for global level javascript variable for it, using the jsId attribute.
<div dojoType="dojo.dnd.Source" jsId="cart" class="target" accept="red,blue" id="target1"> </div>This automatically creates a global variable 'cart' of the type "dojo.dnd.Source".
DnD Events Example
Now let us have a look at simple example of DnD events. In this example, I have created a simple shopping cart. Each blue and red sphere inside the source(shelf) has some price associated with it. This is assigned using the 'dndData' item. You can select multiple spheres from the source(shelf) and drop them onto the target (shopping cart). You will see the updated total price of your cart items. If you drop certain items back to the shelf, the corresponding amount is deducted from the total. I have attached a text file with this article which contains complete source. You will require BLUE.png and RED.png images to run the example.
Below is a excerpt from the file followed by explanation :
<script type="text/javascript">
dojo.require("dojo.dnd.source"); // capital-S Source in Dojo 1.0
var item_price;
var total = 0;
function AddItems(target,nodes)
{
for (var i=0;i<nodes.length;i++)
total += parseFloat((target.getItem(nodes[i].id)).data);
dojo.byId("cost").innerHTML = total;
}
function SubstractItems(target,nodes)
{
for (var i=0;i<nodes.length;i++)
total -= parseInt((target.getItem(nodes[i].id)).data);
dojo.byId("cost").innerHTML = total;
}
function ShowPrice(target,nodes)
{ var sum =0;
for (var i=0;i<nodes.length;i++)
sum += parseInt((target.getItem(nodes[i].id)).data);
dojo.byId("msg").innerHTML = "Selected Item Price is $"+ sum ;
}
function ClearMsg()
{ dojo.byId("msg").innerHTML = "";}
function init()
{
dojo.subscribe("/dnd/drop", function(source,nodes,iscopy){
var t = dojo.dnd.manager().target;
ClearMsg();
if(t == source) return;
if(t == cart)AddItems(t,nodes);
if(t == shelf)SubstractItems(t,nodes);});
dojo.subscribe("/dnd/start", function(source,nodes,iscopy){
var t = dojo.dnd.manager().target;
ShowPrice(source,nodes);});
dojo.subscribe("/dnd/cancel", function(){
ClearMsg();});
}
dojo.addOnLoad( init );
</script>
<div dojoType="dojo.dnd.Source" jsId="shelf" class="source" id="source1" accept="red,blue" singular=false>
<img src="BLUE.png" class="dojoDndItem" dndType="blue" dndData="10" title="$10"/>
<img src="BLUE.png" class="dojoDndItem" dndType="blue" dndData="3" title="$3"/>
.....
</div>
<div dojoType="dojo.dnd.Source" jsId="cart" class="target" accept="red,blue" id="target1"></div>
Total Price (USD): <span id="cost">0.00</span><br/>
<b>Message: <span id="msg" style="color:blue"></span></b>
This was a simple example of handling the DnD events to execute custom defined actions. So far we have covered basics, looked at using CSS for rich user interface and seen how to handle events. In next chapters we will learn about the 'node creator' function and build a very simple web based application to demonstrate use of DnD.
Moveable implements three new events:
Events have better locality than topics: instead of getting called on every move and check if it is "the right" move, you can connect directly to events on the Moveable. Nevertheless topics are still supported.
This text is obsolete in parts. Please refer to the official DnD documentation.
So far we have seen how to use markup to accomplish drag and drop. In this chapter we will have a look at some features, we have not discussed. This chapter features a sample 'Shopping Cart Application'. You can download the related files and refer to the 'readme.txt' to try it out. Everything we will discuss in this chapter has gone into the making of this application.Creating Source/Target with javascript
To do this we need to place two container tags on the page. The following javascript code will create a Source and Target object for us.
var node_creator = function(data, hint){
var types = [];
var node = dojo.doc().createElement("div");
if(data == 'RED'){ types.push("red"); dojo.addClass(node, "redsquare");}
if(data == 'BLUE'){ types.push("blue");dojo.addClass(node, "bluesquare"); }
node.innerHTML = data;
node.id = dojo.dnd.getUniqueId();
return {node: node, data: data, type: types};};
c1 = new dojo.dnd.Source("c1", {creator: node_creator, accept: ["item"],
horizontal: false, copyOnly: false});
c2 = new dojo.dnd.Target("c2", { creator: targetnode_creator,
accept: ["item"]});
c2.insertNodes(false, ["RED","GREEN");
The following points should be noted:
{"id": "S001",
"name": "PENCIL",
"price": "3.20",
"rating": "3",
"description": "A really cool product.",
"img_url": "images/RED.png"}
|
|
|
| Sample JSON from server | HTML node created by the 'node_creator' function. | Customized avatar created using 'node_creator' |
c2.selectAll(); c2.deleteSelectedNodes(); c2.clearItems();
var x = c2.getAllNodes();
for(i =0;i<x.length;i++)
{//jsNode is javascript object for node
//x[i] represents HTML element for the node
var jsnode = c2.getItem(x[i].id);}
| Method | URL | Result |
| GET | Test.php?action=get_all_items | Returns JSON for all items in the shop. This is stored in a plain text file (data.txt). In real world scenario it would come from database. |
| POST | Test.php?action=save_cart_items | Sends JSON for all items in the shopping cart back to server, where they are stored in Session. |
| GET | Test.php?action=get_cart_items | Returns JSON for all shopping cart items, saved by user (with the intent of buying later) .These were saved in the Session. |
| GET | Test.php?action=reset | All saved items in shopping cart are deleted. Session is cleared. |
djConfig="usePlainJson: true"In real life scenario, this could make your page vulnerable to 'JavaScript Hijacking', I haven't bothered about this. It is recommended you use 'text/json-comment-filtered' as mime type while making XHR calls. This document contains good information on the security risks and ways to prevent it.