Sunday, March 2, 2014

HTML5′s drag and drop


For a long time JavaScript functions have existed that allow us to create drag and drop interfaces, but none of these implementations were native to the browser.
In HTML5, we have a native method of creating drag and drop interfaces (with a little help from JavaScript).
I’m going to let you in on how to achieve this…

Browser support

I’d like to get this out of the way before we progress: currently HTML5 drag and drop is supported by all major desktop browsers (including IE (even IE 5.5 has partial support)) but it’s not currently supported by any of the popular mobile browsers.

Drag and drop events

At every stage of the drag and drop operation a different event is fired so that the browser knows what JavaScript code to execute; the events are:
  • dragStart : fires when the user starts dragging the element.
  • dragEnter : fires when the draggable element is first dragged over the target element.
  • dragOver: fires when the mouse is moved over an element when the drag is occurring.
  • dragLeave: fired if the user’s cursor leaves an element when dragging.
  • drag: fires every time we move the mouse during the dragging of our element.
  • drop: fired when the actual drop is performed.
  • dragEnd: fires when the user releases the mouse while dragging the object.
With all of these event listeners you have a lot of control over how your interface works and precisely how it performs in different circumstances.

The dataTransfer object

This is where all the drag and drop magic happens; this object holds the data that was sent by the drag operation. The data can be set and retrieved in various ways, the most important ones are:
  • dataTransfer.effectAllowed=value: this returns the types of action permitted, possible values are none, copy, copyLink, copyMove, link, linkMove, move, all and uninitialized.
  • dataTransfer.setData(format, data): adds the specified data and its format.
  • dataTransfer.clearData( format ): clears all the data for a specific format.
  • dataTransfer.setDragImage(element, x, y): sets the image you wish to drag, the x and y values specify where the mouse cursor should be (0, 0 will place it top left).
  • data = dataTransfer.getData(format) : As the name says it returns the data available for a specific format.

Creating a drag and drop example

Now we will start creating our simple drag and drop example, you can see that we have two small divs and a larger one, we can drag and drop the small ones inside the big one and we can even move them back.

Dragging the object

The first thing we need to do is create our HTML. We make the divs draggable with the draggable attribute, like so:
<div id="boxA" draggable="true"></div>
When this is done we need to define the javascript function that will run when we start to drag this element:
function dragStart(ev) {
   ev.dataTransfer.effectAllowed='move';
   ev.dataTransfer.setData("Text", ev.target.getAttribute('id'));   ev.dataTransfer.setDragImage(ev.target,100,100);
   return true;
}
In this code we first declare what type of effect we allow in the operation and we set that tomove, in the second line we set the data for the operation and in this case the type is Text and the value is the ID of the element we are dragging. After this we use the setDragImage method to set what we will be dragging and then where the cursor will be while dragging, and since the cubes are 200 by 200px I placed that at the very center. Finally we return true.

Dropping the object

In order for an element to accept a drop it needs to listen to 3 different events: dragEnter, dragOver and also the drop event so let’s add this to our html in the div with the ID of big:
<div id="big" ondragenter="return dragEnter(event)" ondrop="return dragDrop(event)" ondragover="return dragOver(event)"></div>
Now that we added event listeners we need to create these functions we will start by the dragenter and dragover events:
function dragEnter(ev) {
   ev.preventDefault();
   return true;
}
function dragOver(ev) {
    ev.preventDefault();
}
In the first function we define what we want to happen when the element we are dragging reaches the element it’s supposed to be dropped in, in this case we only prevent the default behavior of the browser but you can do any number of things like change the background or add some text to indicate that the user is dragging to the correct area and using the dragleave event you can revert the changes you made. Next in the dragOver function we simply prevent the default to allow for the drop.
The next part is where we define the function for when we actually drop the element on the desired target:
function dragDrop(ev) {
   var data = ev.dataTransfer.getData("Text");
   ev.target.appendChild(document.getElementById(data));
   ev.stopPropagation();
   return false;
}
In this last part we first set a variable called data in which we get all the data that is available for the text format and then we append that data (which will be the element that we are dragging) to the div where we wish to drop the element. Finally some final touches like stopping propagation and also returning false.

Making the section a drop target

Checking the demo, you can see we also made sure that the two divs could be dragged back to their original location. Happily, adding another drop target may be simpler than you think; because the functions are already in place, all we have to do is add the event listeners, like so:
<section id="section" ondragenter="return dragEnter(event)" ondrop="return dragDrop(event)" ondragover="return dragOver(event)">
And that is all we need to in order to allow dragging of the divs to the original place.

Conclusion

There are plenty of drag and drop applications built using JavaScript libraries, and it is often simpler to use those libraries. But I hope that, in this HTML5 & JavaScript technique, you see the future potential of the native solution.