Chapter 7. Drag-and-Drop
Drag-and-drop (DnD) can give your application incredible desktop-like functionality and usability that can really differentiate it from the others. This chapter systematically works through this topic, providing plenty of visual examples and source code. You might build off these examples to add some visual flare to your existing application, or perhaps even do something as brave as incorporate the concepts and the machinery that Dojo provides into a DHTML game that people can play online. Either way, this is a fun chapter, so let's get started.
Dragging
While drag-and-drop has been an integral part of desktop applications for more than two decades, web applications have been slow to adopt it. At least part of the reason for the slow adoption is because the DOM machinery provided is quite primitive in and of itself, and the event-driven nature of drag-and-drop makes it especially difficult to construct a unified framework that performs consistently across the board. Fortunately, overcoming these tasks is perfect work for a toolkit, and Dojo provides facilities that spare you from the tedious and time-consuming work of manually developing that boilerplate yourself.
Simple Moveables
Tip
This chapter assumes a minimal working knowledge of CSS. The W3C schools provide a CSS tutorial at http://www.w3schools.com/css/default.asp. Eric Meyer's CSS: The Definitive Guide (O'Reilly) is also a great desktop reference.
As a warm up, let's start out with the most basic example
possible: moving an object[16] around on the screen. Example 7.1, “Simple Moveable” shows the basic page structure that
gets the work done in markup. Take a look, especially at the
emphasized lines that introduce the Moveable class, and then we'll review the
specifics.
Example 7.1. Simple Moveable
<html>
<head>
<title>Fun with Moveables!</title>
<style type="text/css">
.moveable {
background: #FFFFBF;
border: 1px solid black;
width: 100px;
height: 100px;
cursor: pointer;
}
</style>
<script
type="text/javascript"
djConfig="parseOnLoad:true,isDebug:true"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.Moveable");
dojo.require("dojo.parser");
</script>
</head>
<body>
<div class="moveable" dojoType="dojo.dnd.Moveable" ></div>
</body>
</html>As you surely noticed, creating a moveable object on the
screen is quite trivial. Once the Moveable resource was required into the
page, all that's left is to specify an element on the page as being
moveable via a dojoType tag and
parsing the page on load via an option to djConfig. There's really nothing left
except that a bit of style was provided to make the node look a
little bit more fun than an ordinary snippet of text—though a
snippet of text would have worked just as well.
In general, anything you can do by parsing the page when it
loads, you can do programmatically sometime after the page loads.
Here's the very same example, but with a programmatically built
Moveable:
<!-- ... Snip ... -->
<script type="text/javascript">
dojo.require("dojo.dnd.Moveable");
dojo.addOnLoad(function( ) {
var e = document.createElement("div");
dojo.addClass(e, "moveable");
dojo.body( ).appendChild(e);
var m = new dojo.dnd.Moveable(e);
});
</script>
</head>
<body></body>
</html>Table 7.1, “Creating and destroying a Moveable” lists
the methods you need to create and destroy a Moveable.
Table 7.1. Creating and destroying a Moveable
|
Name |
Comment |
|---|---|
|
| The constructor
function that identifies the node that should become
moveable.
|
|
|
Used to disassociate the node with moves, deleting all references so that garbage collection can occur. |
Tip
A Mover is even
lower-level drag-and-drop machinery that Moveable uses internally. Mover objects are not discussed in this
chapter, and are only mentioned for your awareness.
Let's build upon our previous example to demonstrate how to
ensure text-based form elements are editable by setting the skip parameter by building a simple sticky
note on the screen that you can move around and edit. Example 7.2, “Using Moveable to create a sticky note” provides a
working example.
Example 7.2. Using Moveable to create a sticky note
<html>
<head>
<title>Even More Fun with Moveables! </title>
<style type="text/css">
.note {
background: #FFFFBF;
border-bottom: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid black;
width: 302px;
height: 300px;
margin : 0px;
padding : 0px;
}
.noteHandle {
border-left: 1px solid black;
border-right: 1px solid black;
border-top: 1px solid black;
cursor :pointer;
background: #FFFF8F;
width : 300px;
height: 10px;
margin : 0px;
padding : 0px;
}
</style>
<script
type="text/javascript"
djConfig="parseOnLoad:true,isDebug:true"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.Moveable");
dojo.require("dojo.parser");
</script>
</head>
<body>
<div dojoType="dojo.dnd.Moveable" skip=true>
<div class="noteHandle"></div>
<textarea class="note">Type some text here</textarea>
</div>
</body>
</html>Tip
The effect of skip isn't
necessarily intuitive, and it's quite instructive to remove the
skip=true from the outermost
DIV element to see for yourself what happens if you do not specify
that form elements should be skipped.
Although our sticky note didn't
necessarily need to employ drag handles because
the innermost div element was
only one draggable part of the note, we could have achieved the same
effect by using them: limiting a particular portion of the Moveable object to be capable of providing
the drag action (the drag handle) implies that any form elements
outside of the drag handle may be editable. Replacing the emphasized
code from the previous code listing with the following snippet
illustrates:
<div id="note" dojoType="dojo.dnd.Moveable" handle='dragHandle'>
<div id='dragHandle' class="noteHandle"></div>
<textarea class="note">This form element can't trigger drag action</textarea>
</div>Drag Events
It's likely that you'll want to detect when the beginning and
end of drag action occurs for triggering special effects such as
providing a visual cue as to the drag action. Detecting these events
is a snap with dojo.subscribe and
dojo.connect. Example 7.3, “Connecting and subscribing to drag Events” shows another rendition of
Example 7.2, “Using Moveable to create a sticky note”.
Example 7.3. Connecting and subscribing to drag Events
<html>
<head>
<title>Yet More Fun with Moveable!</title>
<style type="text/css">
.note {
background: #FFFFBF;
border-bottom: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid black;
width: 302px;
height: 300px;
margin : 0px;
padding : 0px;
}
.noteHandle {
border-left: 1px solid black;
border-right: 1px solid black;
border-top: 1px solid black;
cursor :pointer;
background: #FFFF8F;
width : 300px;
height: 10px;
margin : 0px;
padding : 0px;
}
.movingNote {
background : #FFFF3F;
}
</style>
<script
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.Moveable");
dojo.addOnLoad(function( ) {
//create and keep references to Moveables for connecting later.
var m1 = new dojo.dnd.Moveable("note1", {handle : "dragHandle1"});
var m2 = new dojo.dnd.Moveable("note2", {handle : "dragHandle2"});
// system-wide topics for all moveables.
dojo.subscribe("/dnd/move/start", function(node){
console.log("Start moving", node);
});
dojo.subscribe("/dnd/move/stop", function(node){
console.log("Stop moving", node);
});
// highlight note when it moves...
//connect to the Moveables, not the raw nodes.
dojo.connect(m1, "onMoveStart", function(mover){
console.log("note1 start moving with mover:", mover);
dojo.query("#note1 > textarea").addClass("movingNote");
});
dojo.connect(m1, "onMoveStop", function(mover){
console.log("note1 stop moving with mover:", mover);
dojo.query("#note1 > textarea").removeClass("movingNote");
});
});
</script>
</head>
<body>
<div id="note1">
<div id='dragHandle1' class="noteHandle"></div>
<textarea class="note">Note1</textarea>
</div>
<div id="note2">
<div id='dragHandle2' class="noteHandle"></div>
<textarea class="note">Note2</textarea>
</div>
</body>
</html>Tip
In the dojo.query
function calls, you should recall that the parameter "#note1 > textarea" means to return
the textarea nodes that are
children of the node with an id
of "note1". See Table 5.1, “Commonly used CSS selectors” for a summary of common
CSS3 selectors that can be passed into dojo.query.
Note from the previous code listing that you do not connect
to the actual node of interest. Instead, you connect to the
Moveable that is returned via a
programmatic call to create a new dojo.dnd.Moveable.
As you can see, it is possible to subscribe to global drag
events via pub/sub style communication or zero in on specific events
by connecting to the particular Moveable nodes of interest. Table 7.2, “Moveable events” summarizes the events that you may
connect to via dojo.connect.
For pub/sub style communication, you can use dojo.subscribe to subscribe to the
"dnd/move/start" and "dnd/move/stop" topics.
Table 7.2. Moveable events
|
Event |
Summary |
|---|---|
|
|
Called before every move. |
|
|
Called after every move. |
|
|
Called during the very first move; handy for performing initialization routines. |
|
Called during every
move notification; by default, calls |
|
Called just before
|
|
Called just after
|
Z-Indexing
Our working example with sticky notes is growing increasingly
sophisticated, but one noticeable characteristic that may become an
issue is that the initial z-indexes of the notes do not change: one
of them is always on top and the other is always on the bottom. It
might seem more natural if the note that was last selected became
the note that is on top, with the highest z-index. Fortunately, it
is quite simple to adjust z-index values in a function that is fired
off via a connection to the onMoveStartEvent.
The solution presented below requires modifying the addOnLoad function's logic and is somewhat
elegant in that it uses a closure to trap a state variable instead
of explicitly using a module-level or global variable:
dojo.addOnLoad(function( ) {
//create and keep references to Moveables for connecting later.
var m1 = new dojo.dnd.Moveable("note1", {handle : "dragHandle1"});
var m2 = new dojo.dnd.Moveable("note2", {handle : "dragHandle2"});
var zIdx = 1; // trapped in closure of this anonymous function
dojo.connect(m1, "onMoveStart", function(mover){
dojo.style(mover.host.node, "zIndex", zIdx++);
});
dojo.connect(m2, "onMoveStart", function(mover){
dojo.style(mover.host.node, "zIndex", zIdx++);
});
});Warning
Recall from Chapter 2, Language and Browser Utilities that dojo.style requires the use of DOM
accessor formatted properties, not stylesheet formatted
properties. For example, trying to set a style property called
"z-index" would not
work.
Constrained Moveables
Being able to move a totally unconstrained object around on the screen with what amounts to a trivial amount of effort is all fine and good, but sooner than later, you'll probably find yourself writing up logic to define boundaries, restrict overlap, and define other constraints. Fortunately, the drag-and-drop facilities provide additional help for reducing the boilerplate you'd normally have to write for defining drag-and-drop constraints.
There are three primary facilities included in dojo.dnd that allow you to constrain your
moveable objects: writing your own custom constraint function that
dynamically computes a bounding box (a constrainedMoveable ), defining a static
boundary box when you create the moveable objects (a boxConstrainedMoveable ), and constraining
a moveable object within the boundaries defined by another parent
node (a parentConstrainedMoveable
). The format for each type of boundary box follows the same
conventions as are described in Chapter 2, Language and Browser Utilities in the section "The Box
Model."
Here's a modification of our previous sticky note example to
start out with a constrainedMoveable :
<html>
<head>
<title>Moving Around</title>
<style type="text/css">
.note {
background: #FFFFBF;
border-bottom: 1px solid black;
border-left: 1px solid black;
border-right: 1px solid black;
width: 302px;
height: 300px;
margin : 0px;
padding : 0px;
}
.noteHandle {
border-left: 1px solid black;
border-right: 1px solid black;
border-top: 1px solid black;
cursor :pointer;
background: #FFFF8F;
width : 300px;
height: 10px;
margin : 0px;
padding : 0px;
}
.movingNote {
background : #FFFF3F;
}
#note1, #note2 {
width : 302px
}
</style>
<script
type="text/javascript"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.Moveable");
dojo.require("dojo.dnd.move");
dojo.addOnLoad(function( ) {
var f1 = function( ) {
//clever calculations to define a bounding box.
//keep note1 within 50 pixels to the right/bottom of note2
var mb2 = dojo.marginBox("note2");
b = {};
b["t"] = 0;
b["l"] = 0;
b["w"] = mb2.l + mb2.w + 50;
b["h"] = mb2.h + mb2.t + 50;
return b;
}
var m1 = new dojo.dnd.move.constrainedMoveable("note1",
{handle : "dragHandle1", constraints : f1, within : true});
var m2 = new dojo.dnd.Moveable("note2", {handle : "dragHandle2"});
var zIdx = 1;
dojo.connect(m1, "onMoveStart", function(mover){
dojo.style(mover.host.node, "zIndex", zIdx++);
});
dojo.connect(m2, "onMoveStart", function(mover){
dojo.style(mover.host.node, "zIndex", zIdx++);
});
});
</script>
</head>
<body>
<div id="note1">
<div id='dragHandle1' class="noteHandle"></div>
<textarea class="note">Note1</textarea>
</div>
<div id="note2">
<div id='dragHandle2' class="noteHandle"></div>
<textarea class="note">Note2</textarea>
</div>
</body>
</html>Warning
When computing bounding boxes for Moveable objects, ensure that you have
explicitly defined a height and width for the outermost container
of what is being moved around on the screen. For example, leaving
the outermost div that is the
container for our sticky note unconstrained in width produces
erratic results because the moveable div is actually much wider than the
yellow box that you see on the screen. Thus, attempting to compute
constraints using its margin box does not function as
expected.
To summarize, an explicit boundary was defined for the note's
outermost div so that its margin
box could be computed with an accurate width via dojo.marginBox, and a custom constraint
function was written that prevents note1 from ever being more than 50 pixels
to the right and to the bottom of note2.
Warning
Attempting to use a constrainedMoveable without specifying a
constraint function produces a slew of errors, so if you decide
not to use a constraint function, you'll need to revert to using a
plain old Moveable.
Defining a static boundary for a Moveable is even simpler. Instead of
providing a custom function, you simply pass in an explicit
boundary. Modify the previous example to make note2 a boxConstrainedMoveable with the following
change and see for yourself:
var m2 = new dojo.dnd.move.boxConstrainedMoveable("note2",
{
handle : "dragHandle2",
box : {l : 20, t : 20, w : 500, h : 300}
});As you can see, the example works as before, with the
exception that note2 cannot move
outside of the constraint box defined.
Finally, a parentConstrainedMoveable works in a
similar fashion. You simply define the Moveable s and ensure that the parent node
is of sufficient stature to provide a workspace. No additional work
is required to make the parent node a special kind of Dojo class.
Here's another revision of our working example to illustrate:
<!-- ... snip ... --> .parent { background: #BFECFF; border: 10px solid lightblue; width: 400px; height: 700px; padding: 10px; margin: 10px; } <!-- ... snip ... --> <script type="text/javascript"> dojo.require("dojo.dnd.move"); dojo.addOnLoad(function() { new dojo.dnd.move.parentConstrainedMoveable("note1", { handle : "dragHandle1", area: "margin", within: true }); new dojo.dnd.move.parentConstrainedMoveable("note2", { handle : "dragHandle2", area: "padding", within: true }); }); </script> </head> <body> <div class="parent" > <div id="note1"> <div id='dragHandle1' class="noteHandle"></div> <textarea class="note">Note1</textarea> </div> <div id="note2"> <div id='dragHandle2' class="noteHandle"></div> <textarea class="note">Note2</textarea> </div> </div> </body> </html>
The area parameter for
parentConstrainedMoveable s is of
particular interest. You may provide "margin", "padding", "content", and "border" to confine the Moveable s to the parent's area.
Tip
Like ordinary Moveable s,
you can connect to specific objects or use pub/sub style
communication to detect global drag-and-drop events. Because
constrainedMoveable and
boxConstrainedMoveable inherit
from Moveable, the event names
for dojo.connect and dojo.subscribe are the same as outlined
in Table 7.2, “Moveable events” for Moveable.
Dropping
Thus far, this chapter has focused on
dragging objects around on the screen. This
section wraps up the discussion by focusing in on the
dropping part of it all. To get started, let's
first take a look at dojo.dnd.Source, a special container class
the toolkit provides a drag-and-drop source. A Source can also act as a target for a drop,
but as we'll see in a moment, you can also specify a "pure" target
with dojo.dnd.Target. While a
Source may act as an origin and a
destination, a Target may only act
as a destination.
Creating a Source is just
like creating a Moveable ; you call
the constructor function and pass in a node as the first argument and
an Object of parameters as the
second argument, like so. Table 7.3, “Creating and destroying a Source” lists the relevant
methods.
Table 7.3. Creating and destroying a Source
|
Name |
Comment |
|---|---|
|
|
Constructor method for
creation. Valid values for |
|
|
Prepares the |
Table 7.4, “Configuration parameters for Source's params in Table 7.3, “Creating and destroying a Source””
summarizes key parameters involved in the creation of a Source object.
Table 7.4. Configuration parameters for Source's params in Table 7.3, “Creating and destroying a Source”
|
Parameter |
Type |
Comment |
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
A very common use for a Source is to eliminate some of the
bookkeeping that is involved in dragging and dropping items that are
arranged in a list-like format. The following code example
illustrates:
<html>
<head>
<title>Fun with Source!</title>
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dnd.css" />
<link rel="stylesheet" type="text/css"
href="dndDefault.css" />
<script
type="text/javascript"
djConfig="parseOnLoad:true"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.Source");
dojo.require("dojo.parser");
</script>
</head>
<body>
<div dojoType="dojo.dnd.Source" class="container">
<div class="dojoDndItem">foo</div>
<div class="dojoDndItem">bar</div>
<div class="dojoDndItem">baz</div>
<div class="dojoDndItem">quux</div>
</div>
</body>
</html>Although this initial example may not look like much, there is a
tremendous amount of functionality packed into it. For starters,
notice that the only Dojo class that is directly involved is Source, and to create a container of items
that are drag-and-droppable within that container, you simply provide
the token dojoType tag and ensure
that the element is parsed; like most other examples, the parseOnLoad parameter passed to djConfig takes care of this task.
Next, take a few moments to tinker around with the example. It might be obvious that you can drag-and-drop single items at a time, but it's important to also note the full gamut of functionality that is offered:
Clicking selects a single element and results in all other elements becoming unselected.
Ctrl-clicking toggles the selection state of an item and allows you to build up multiple items at a time; you can also deselect individual items from a multiple selection situation.
Shift-clicking selects a range of elements from the previous-most selection to the current element being clicked. Any selections before the previous-most selection become unselected.
Ctrl-Shift-clicking selects a range of elements from the previous-most selection to the current element being clicked, but preserves any selections before the previous-most selection.
Holding down the Ctrl key while performing a drop results in the selection(s) being copied. Figure 7.1, “dnd1 shows an initial selection using Ctrl-click; dnd2 is the result of performing a Shift-click on quux; dnd3 is the result of performing a Shift-Ctrl-click on quux; dnd4 depicts a move operation by dragging without the Ctrl key; and dnd5 shows a copy operation by dragging with the Ctrl key applied” illustrates some of these actions.
Figure 7.1. dnd1 shows an initial selection using Ctrl-click; dnd2 is the result of performing a Shift-click on quux; dnd3 is the result of performing a Shift-Ctrl-click on quux; dnd4 depicts a move operation by dragging without the Ctrl key; and dnd5 shows a copy operation by dragging with the Ctrl key applied
Pure Targets
As mentioned earlier in the chapter, there are bound to be
plenty of times when you'll need to employ a Target that can only act as a destination;
once items are placed in it, they may not be moved or reordered.
Make the following trivial modification to the previous code listing
to see a Target in
action.
<body>
<div dojoType="dojo.dnd.Source" class="container">
<div class="dojoDndItem">foo</div>
<div class="dojoDndItem">bar</div>
<div class="dojoDndItem">baz</div>
<div class="dojoDndItem">quux</div>
</div>
<!-- Items added to targets cannot be removed or reordered -->
<div dojoType="dojo.dnd.Target" class="container"></div>
</body>As you may be able to tell by now, a tremendous amount of
functionality is wrapped up into just a few lines of code, and
although div elements were used
for the example, note that other types of standard HTML elements
work equally well. Unordered lists via the ul and li elements are a common choice.
Custom Avatars
The small icon that temporarily appears when an item from a
Source is being moved around is
called an avatar. Although the standard avatar
is quite nice, you may want to construct your own at some point. The
following code adjustment illustrates how to define custom text for
an avatar by overriding the creator method because this method is used
to create an avatar representation of one or more nodes. In this
particular circumstance, we'll choose to override creator in markup. The layout is also
adjusted to a horizontal specification to simultaneously demonstrate
how to adjust a layout:
<body>
<div dojoType="dojo.dnd.Source" horizontal=true class="container">
<span class="dojoDndItem ">foo</span>
<span class="dojoDndItem ">bar</span>
<span class="dojoDndItem ">baz</span>
<span class="dojoDndItem ">quux</span>
<script type="dojo/method" event="creator" args="item,hint">
// override the creator function and return the appropriate type
var node = dojo.doc.createElement("span");
node.id = dojo.dnd.getUniqueId( );
node.className = "dojoDndItem";
node.innerHTML = "<strong style='color: red'>Custom</strong> "+item;
return {node: node, data: item, type: ["text"]};
</script>
</div>
<div dojoType="dojo.dnd.Target" horizontal=true class="container"></div>
</body>Note that the arguments passed into creator are item and hint, the actual item being moved and a
value specifying a "hint" for the kind of representation of that
should be created. Unless you implement your own low-level
machinery, hint will always be
"avatar". The creator function is expected to return an
object representation of item
with keys for an actual DOM node, a data representation, and the
type of representation. Recall that "text" is the default representation
accepted by a Source
object.
Drop Events
Subscribing and connecting to events via dojo.subscribe and dojo.connect works just as easy as with
Moveable objects. Table 7.5, “Drop events” summarizes public events for pub/sub
and connection-style communications, and a code example
follows.
Table 7.5. Drop events
|
Type |
Event |
Parameters |
Summary |
|---|---|---|---|
|
|
|
|
Published when the
mouse moves over a |
|
|
|
|
Published when a drag
beings. Parameter |
|
|
|
|
Published when a drop
occurs (and a drag officially) ends. Parameter |
|
|
|
N/A |
Published when a drop operation is cancelled (for example, when the Esc key is pressed). |
|
|
|
|
Called when a mouse
moves over a |
|
|
|
|
Called when a drag
begins. Parameter |
|
|
|
|
Called when a drop
occurs (and a drag officially) ends. Parameter |
|
|
|
N/A |
Called when a drop operation is cancelled (for example, when the Esc key is pressed). |
Go ahead and load up the following full-blown example and use
Firebug to inspect the output that occurs from the various topics
that we subscribe to and log to the console, and remember that you
can drag-and-drop from different Source containers. Figure 7.2, “Firebug is great for learning the ropes of
drag-and-drop” shows the result. Good
stuff!
<html>
<head>
<title>More Fun with Drop!</title>
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dijit/themes/tundra/tundra.css" />
<link rel="stylesheet" type="text/css"
href="http://o.aolcdn.com/dojo/1.1/dojo/resources/dojo.css" />
<link rel="stylesheet" type="text/css"
href="dndDefault.css" />
<script
type="text/javascript"
djConfig="parseOnLoad:true"
src="http://o.aolcdn.com/dojo/1.1/dojo/dojo.xd.js">
</script>
<script type="text/javascript">
dojo.require("dojo.dnd.Source");
dojo.require("dojo.parser");
dojo.addOnLoad(function( ) {
dojo.subscribe("/dnd/source/over", function(source) {
console.log("/dnd/source/over", source);
});
dojo.subscribe("/dnd/start", function(source, nodes, copy) {
console.log("/dnd/start", source, nodes, copy);
});
dojo.subscribe("/dnd/drop", function(source, nodes, copy) {
console.log("/dnd/drop", source, nodes, copy);
});
dojo.subscribe("/dnd/cancel", function( ) {
console.log("/dnd/cancel");
});
});
</script>
</head>
<body>
<div id="source1" dojoType="dojo.dnd.Source" class="container">
<div class="dojoDndItem">foo</div>
<div class="dojoDndItem">bar</div>
<div class="dojoDndItem">baz</div>
<div class="dojoDndItem">quux</div>
</div>
<div id="source2" dojoType="dojo.dnd.Source" class="container">
<div class="dojoDndItem">FOO</div>
<div class="dojoDndItem">BAR</div>
<div class="dojoDndItem">BAZ</div>
<div class="dojoDndItem">QUUX</div>
</div>
</body>
</html>All it takes to demonstrate some connections is a different
addOnLoad function. Note that
because we need to have a reference to the Source that is created (not the DOM node),
we need to programmatically create the Source instead of relying on the parser to
instantiate widgets that are defined in markup. Substitute the
following, turn off djConfig's
parseOnLoad flag, and take a look
at the Firebug console once again:
dojo.addOnLoad(function( ) {
//keep a reference to the Source to use for connecting.
var s1 = new dojo.dnd.Source("source1");
dojo.connect(s1, "onDndSourceOver", function(source) {
console.log("onDndSourceOver for", s1, source);
});
dojo.connect(s1, "onDndStart", function(source, nodes, copy) {
console.log("onDndStart for ", s1, source, nodes, copy);
});
dojo.connect(s1, "onDndStop", function(source, nodes, copy, target) {
console.log("onDndStop for", s1, source, nodes, copy, target);
});
dojo.connect(s1, "onDndCancel", function( ) {
console.log("onDndCancel for ", s1);
});
});Scripting Droppables
While the previous example demonstrated that you could use the
Source constructor function to
make a node droppable, there is considerably more functionality you
can achieve via scripting. Table 7.6, “Selector API”
summarizes the functionality that Selector, a lower level class in dojo.dnd, offers. Because Source inherits from Selector, these functions are directly
available to you though Source,
although you might very well find uses for Selector in and of itself.
Table 7.6. Selector API
|
Method |
Comment |
|---|---|
|
|
Returns an |
|
|
Deselects all of the nodes. |
|
|
Selects all of the nodes. |
|
|
Deletes all selected nodes. |
|
|
Inserts an |
|
|
Prepares the object for garbage collection. |
|
|
Can be connected to
via |
|
|
Can be connected to
via |
|
|
Can be connected to
via |
|
|
Can be connected to
via |
|
|
Can be connected to
via |
Summary
Tip
For a really practical example of drag-and-drop at work, be
sure to check out ""Drag-and-Drop with the Tree" in Chapter 15, Application Widgets, where drag-and-drop is applied to
the problem of manipulating the Tree dijit. The Tree is a phenomenal piece of engineering
and dnd only makes it
better!
After reading this chapter, you should be able to:
Construct unconstrained
Moveableobjects and drag them around on the screenDefine constraints for
Moveableobjects to control their behaviorBe able to implement
SourceandTargetcontainers to create collections of items that can be dragged and dropped to/from/within one anotherCreate custom avatars to communicate with the user when a drop operation is about to occur
Use
dojo.connectanddojo.subscribeto receive event notifications for drag-and-drop objects
Next, we'll cover animation and special effects.
[16] The term object is used in this chapter to generically refer to a moveable DOM node. This usage implies nothing whatsoever about objects from object-oriented programming.







Add a comment



Add a comment