Being able to drag HTML elements around a page and have them stay where you leave them is visually interesting. To make this behavior truly useful, though, an event should be thrown when the drop occurs. Furthermore, the event that is thrown should depend on where the drop occurs. In other words, there needs to be behavior that can be added to a given HTML element that will turn it into a “dropzone” or a “drop target”, the same way that the floating behavior can be added to an HTML div
tag to turn it into a drag and drop element.
In the following examples, I will show how Atlas supports the concept of dropzones. In its current state, Atlas does not support an out-of-the-box behavior for creating dropzone elements in quite the same way it does for floating elements. It does, however, implement behaviors for a DragDropList
element and a DraggableListItem
element which, when used together, allow you to create lists that can be reordered by dragging and dropping. If you would like to explore this functionality some more, there are several good examples of using the DragDropList
behavior on the web, for instance, Introduction to Drag And Drop with Atlas.
The main disadvantage of the dragdropzone behavior is that it only works with items that have been decorated with the DragDropList
behavior. The functionality that this puts at your disposal is fairly specific. To get the sort of open-ended dropzone functionality I described above, that will also work with the predefined floating behavior, you will need to write your own dropzone behavior class in JavaScript. Fortunately, this is not all that hard.
Atlas adds several OOP extensions to JavaScript in order to make it more powerful, extensions such as namespaces, abstract classes, and interfaces. You will take advantage of these in coding up your own dropzone behavior. If you peer behind the curtain and look at the source code in the AtlasUIDragDrop.js file, (you can do this with the Visual Studio debugger), you will find several interfaces defined there, including one for Sys.UI.DragSource
and one for Sys.UI.DropTarget
. In fact, both the FloatingBehavior
class and the DraggableListItem
class implement the Sys.UI.DragSource
interface, while Sys.UI.DropTarget
is implemented by the DragDropList
class. The code for these two interfaces looks like this:
Sys.UI.IDragSource = function() {
this.get_dataType = Function.abstractMethod;
this.get_data = Function.abstractMethod;
this.get_dragMode = Function.abstractMethod;
this.onDragStart = Function.abstractMethod;
this.onDrag = Function.abstractMethod;
this.onDragEnd = Function.abstractMethod;
}
Sys.UI.IDragSource.registerInterface(‘Sys.UI.IDragSource’);
Sys.UI.IDropTarget = function() {
this.get_dropTargetElement = Function.abstractMethod;
this.canDrop = Function.abstractMethod;
this.drop = Function.abstractMethod;
this.onDragEnterTarget = Function.abstractMethod;
this.onDragLeaveTarget = Function.abstractMethod;
this.onDragInTarget = Function.abstractMethod;
}
Sys.UI.IDropTarget.registerInterface(‘Sys.UI.IDropTarget’);
Why do you need to implement these interfaces instead of simply writing out brand new classes to support drag, drop, and dropzones? The secret is that, behind the scenes, a third class, called the DragDropManager
, is actually coordinating the interactions between the draggable elements and the dropzone elements, and it only knows how to work with classes that implement the IDragSource
or the IDropTarget
. The DragDropManager
class registers which dropzones are legitimate targets for each draggable element, handles the MouseOver
events to determine when a dropzone has a draggable element over it, and a hundred other things you do not want to do yourself. In fact, it does it so well that the dropzone behavior you are about to write is pretty minimal. First, create a new JavaScript file called DropZoneBehavior.js. I placed my JavaScript file under a subdirectory called scriptLibrary, but this is not necessary in order to make the dropzone behavior work. Next, copy the following code into your file:
Type.registerNamespace(‘Custom.UI’);
Custom.UI.DropZoneBehavior = function() {
Custom.UI.DropZoneBehavior.initializeBase(this);
this.initialize = function() {
Custom.UI.DropZoneBehavior.callBaseMethod(this, ‘initialize’);
Sys.UI.DragDropManager.registerDropTarget(this);
}
this.dispose = function() {
Custom.UI.DropZoneBehavior.callBaseMethod(this, ‘dispose’);
}
this.getDescriptor = function() {
var td = Custom.UI.DropZoneBehavior.callBaseMethod(this, ‘getDescriptor’);
return td;
}
this.get_dropTargetElement = function() {
return this.control.element;
}
this.drop = function(dragMode, type, data) {
alert(‘dropped’);
}
this.canDrop = function(dragMode, dataType) {
return true;
}
this.onDragEnterTarget = function(dragMode, type, data) {
}
this.onDragLeaveTarget = function(dragMode, type, data) {
}
this.onDragInTarget = function(dragMode, type, data) {
}
}
Custom.UI.DropZoneBehavior.registerClass(‘Custom.UI.DropZoneBehavior’,
Sys.UI.Behavior, Sys.UI.IDragSource,
Sys.UI.IDropTarget, Sys.IDisposable);
Sys.TypeDescriptor.addType(‘script’, ‘DropZoneBehavior’,
Custom.UI.DropZoneBehavior);
I need to explain this class a bit backwards. The first thing worth noticing is the second to last line that begins with “Custom.UI.DropZoneBehavior.registerClass
“. This is where the dropZoneBehaviorClass
defined above gets registered with Atlas. The first parameter of the registerClass
method takes the name of the class. The second parameter takes the base class. The remaining parameters take the interfaces that are implemented by the new class. The line following this makes your class available for declarative markup scripting. Now back to the top, the “Type.registerNamespace
” method allows you to register your custom namespace. The next line declares our new class using an anonymous method syntax. This is a way of writing JavaScript that I am not particularly familiar with, but is very important for making JavaScript object oriented, and is essential for designing Atlas behaviors. Within the anonymous method, the class methods Initialize
, Dispose
, and getDescriptor
are simply standard methods used for all behavior classes, and in this implementation, all you need to do is call the base method (that is, the method of the base class that you specify in the second to last line of this code sample.) The only thing special you do is to register the drop target with the Sys.UI.DragDropManager
in the Initialize
method. This is the act that makes much of the drag drop magic happen.
Next, you implement the IDropTarget
methods. In this example, you are only implementing two methods, “this.canDrop
” and “this.drop
“. For “canDrop
“, you are just going to return true
. More interesting logic can be placed here to determine which floating div
tags can actually be dropped on a given target, and even to determine what sorts of floating div
s will do what when they are dropped; but in this case, you only want a bare-bones implementation of IDropTarget
that will allow any floating div
to be dropped on it. Your implementation of the “drop” method is similarly bare bones. When a floating element is dropped on one of your drop targets, an alert message will be thrown indicating that something has occurred. And that’s about it. You now have a drop behavior that works with the floating behavior we used in the previous examples.
You should now write up a page to show off your new custom dropzone behavior. You can build on the previous samples to accomplish this. In the Atlas Script Manager, besides registering the AtlasUIDragDrop script, you will also want to register your new DropZoneBehavior script:
<atlas:ScriptManager ID=”ScriptManager1″ runat=”server”>
<Scripts>
<atlas:ScriptReference ScriptName=”AtlasUIDragDrop” />
<atlas:ScriptReference Path=”scriptLibrary/DropZoneBehavior.js” />
</Scripts>
</atlas:ScriptManager>
Next, you will want to add a new div
tag to the HTML body, that can be used as a drop target:
<div style=”background-color:Red;height:200px;width:200px;”>
<div id=”draggableDiv” style=”height:100px;width:100px;background-color:Blue;”>
<div id=”handleBar” style=”height:20px;width:auto;background-color:Green;”>
</div>
</div>
</div>
<div id=”dropZone” style=”background-color:cornflowerblue;height:200px;width:200px;”>
Drop Zone
</div>
Finally, you need to add a declarative markup element to add your custom DropZone behavior to the div
you plan to use as a dropzone element. The XML markup should look like this:
<script type=”text/xml-script”>
<page xmlns:script=”http://schemas.microsoft.com/xml-script/2005″>
<components>
<control id=”dropZone”>
<behaviors>
<DropZoneBehavior/>
</behaviors>
</control>
<control id=”draggableDiv”>
<behaviors>
<floatingBehavior handle=”handleBar”/>
</behaviors>
</control>
</components>
</page>
</script>
The code you have just written should basically add a dropzone to the original declarative drag and drop example. When you drop your drag element on the dropzone, an alert message should appear. You can expand on this code to make the drop method of your custom dropzone behavior do much more interesting things, such as firing off other JavaScript events in the current page, or even calling a webservice, using Atlas, that will in turn process server-side code for you.