twitter rss feed

html5 laboratory

The drag and drop API

The experiment

Drag and drop is one of the often used functionalities throughout websites for various reasons. Normally some complex JavaScript or a 3rd party library is required for this, but with HTML5 there is a new drag and drop API that makes it much easier to implement.

This experiment will briefly talk about various parts of the drag and drop API and will also show how to implement a very simple drag and drop example, where single words can be dragged from one box to another.

The process

To begin with, as is usual, let's take a quick look at the drag and drop API basics. There are quite a lot of them though, so bear with me!

Browser support

As always with a new HTML5 feature, there's an element of "what browser supports this now" and the drag and drop API is no different. As of writing, the latest versions of Firefox and Chrome support this API.

Making an item draggable

The first thing that is required is to make an item draggable. This is done using the draggable attribute. Almost anything can be made draggable provided it has this attribute set to true. Naturally it's default setting is false. The following code defines a div that contains the word 'the' and allows it to be dragged:

<div id="word1" draggable="true">the</div>

The second thing that must be done is to add a listener for the dragstart event and set the drag data within this listener. This can be applied to the above <div> as follows:

<div id="word1" draggable="true" ondragstart="event.dataTransfer.setData('text/plain','the');">the</div>

You can also add a listener to the dragend event to run some code when the drag is complete. This is done in the same manner as above, except that the code it added to dragend.

The dataTransfer object

All drag events must record drag data in an object called dataTransfer. As with all JavaScript objects, it has a number of properties and methods, but I will mention only a few here. For a complete list, check out Mozilla's DataTransfer.

Properties

Property Type Description
dropEffect string indicates which effect will be used for the drag operation (see dropEffect)
effectAllowed string indicates which effects are allowed for this drag operation (see effectAllowed)

Methods

Method Arguments Returns Description
getData String type String Returns the data for a given type (see getData())
setData String type, String data void Sets the data for a given type (see setData())
setDragImage String type, String data void Sets the image to be used for dragging if a custom once is desired (see setDragImage())

Starting a drag operation

When a drag operation on a draggable object begins, a number of things need to be done:

  • define the drag effect that is allowed. There are three effects that can be allowed:
    • copy — indicates that the item being dragged will be copied
    • move — indicates that the item being dragged is to be moved
    • link — indicates that some sort of relationship is to be created between the original item and the resultant one.
    Whilst the item is being dragged, the drag effects can be altered to indicate that certain effects are allowed at certain locations. For more information, see Drag Effects.
  • set the data that is to be dragged using setData on the dataTransfer object
  • define the drag image using setDragImage, if it's to be customised
<div id="word1" draggable="true" ondragstart="return dragDefine(event);">the</div>

where dragDefine is defined as:

function dragDefine(ev) {
   ev.dataTransfer.effectAllowed = 'move';
   ev.dataTransfer.setData("text/plain", ev.target.getAttribute('id'));
   ev.dataTransfer.setDragImage(ev.target, 0, 0);
   return true;
}

Defining the drop targets

Obviously the draggable items need somewhere where they can be dragged from and dropped into. This can simply be a <div>, which is what we will use. Listeners need to be attached to a number of events to define what to do when the events fire.

Event Description
ondragover fires when a draggable item is dragged over the drop target
ondrop fires when the draggable item is dropped onto the drop target

Since most areas of a web page are not valid places to drop items, the default behaviour is to not allow drops and so this must be overriden. This is achieved by cancelling the event. This can be done by either returning false, or by calling the event's event.preventDefault() method.

So we can define a drop area as follows:

<div id="boxA" ondragover="dragOver(event)" ondrop="dragDrop(event)"></div>

where dragOver() and dragDrop() are defined as follows:

function dragOver(ev) {
   // Turn off the default behaviour i.e. allow the drop
   ev.preventDefault();
}

function dragDrop(ev) {
   // Get the id of the item being dragged
   var idDrag = ev.dataTransfer.getData("Text");
   // Append the dragged item to the item it is currently over
   ev.target.appendChild(document.getElementById(idDrag));
   // Turn off the default behaviour 
   ev.preventDefault();
}

As you can see, when the item is being dropped the id of the dragged item is obtained and then appended to the target item and thus the item is dragged and dropped. Again the default behaviour is switched off as otherwise the browser would try to load the contents of the dragged item!

The results

So, putting all this together, as usual I have created a HTML5 drag and drop test file for you to see a simple drag and drop operation in action.

Note that this will only work in the latest versions of Firefox and Chrome. It can be made to work in Internet Explorer, an example of which can be seen at HTML5 Demos - Drag.

The functions defined in the code below are as defined in the experiment process above. There are two drop target boxes defined and five draggable text divs which can be dragged and dropped in either box:

<div id="boxA" ondragover="dragOver(event)" ondrop="dragDrop(event)">
   <div id="word1" class="word" draggable="true" ondragstart="return dragDefine(event)" ondragend="dragEnd(event)">HTML5</div>
   <div id="word2" class="word" draggable="true" ondragstart="return dragDefine(event)" ondragend="dragEnd(event)">is</div>
   <div id="word3" class="word" draggable="true" ondragstart="return dragDefine(event)" ondragend="dragEnd(event)">very</div>
   <div id="word4" class="word" draggable="true" ondragstart="return dragDefine(event)" ondragend="dragEnd(event)">useful</div>
   <div id="word5" class="word" draggable="true" ondragstart="return dragDefine(event)" ondragend="dragEnd(event)">indeed</div>
</div>
<div id="boxB" ondragover="return dragOver(event)" ondrop="return dragDrop(event)"></div>