Native HTML5 Drag and Drop

Supported Browsers: Opera none, Firefox 3.5+, Safari 3.2+, IE 7.0+, Chrome 9.0+, iPhone none, Android 2.1+

By Alexander Jones,

Introduction

HTML5 comes with a Drag and Drop API that brings native DnD support to the browser making it much easier to implement. It’s based on Microsoft’s original implementation for Internet Explorer 5, which is now currently supported in all the modern browsers.
Drag and Drop is a feature which allows a user to click and hold the mouse button down over an element, drag it to another location, and release the mouse button to drop the element there. A translucent representation of what is being dragged will follow the mouse pointer during the drag operation.
The specification defines an event-based mechanism, javaScript API and additional mark-up for declaring that just about any type of element be ‘draggable’ on a page. Native browser DnD means faster, more responsive web apps.

1. Browser Support

Browser support is an important feature in any usage of HTML5, not just DnD. Enabling users to have fully functioning web apps as intended by the developer is vital.

Eric Bidelman - Developer Relations, Google discusses in his Native HTML5 Drag and Drop Article: I don't think anyone can argue against native browser support for a particular feature. Native browser DnD means faster, more responsive web apps. Many apps that utilize DnD would have a poor experience without it. For example, imagine a chess game pieces that don't move!

Feature Detection

An app or function that utilises DnD would provide the user with a poor experience if it wasn’t supported in the particular browser. Although browser support is fairly complete, determining if a browser implements DnD is important for providing a working solution that degrades well for the user. When DnD isn't available, use an available library fallback to maintain a working app.
Modernizr provides a great way of detecting feature support, setting a boolean property for each feature it tests:

if (Modernizr.draganddrop) {
    // Browser supports HTML5 DnD.
    } else {
    // Fallback to a library solution.
}

2. Creating Draggable Content

Making an object draggable can be done by using the draggable=true attribute on the element that needs to be moveable, this can include images, files, links any many other things.
As an example, let's start creating rearrangeable columns. The basic markup may look something like this:

<div id="boxes">
    <div class="box" draggable="true"><header>1</header></div>
    <div class="box" draggable="true"><header>2</header></div>
    <div class="box" draggable="true"><header>3</header></div>
</div>

An important point that in most modern browsers, text selections, img elements, and anchor elements with a href attribute are draggable by default. For example, dragging the logo on google.co.uk produces a ghost image:

Google Image Drag
Google Image Drag

This image can be dropped in the address bar, a <input type="file" /> element, or even the desktop. Using some CSS3 effects it's possible to make our mark-up look like boxes. Adding cursor: move gives users a visual indicator that something is moveable:

<style>
[draggable] {
    -moz-user-select: none;
    -khtml-user-select: none;
    -webkit-user-select: none;
    user-select: none;
}
.box {
    height: 125px;
    width: 125px;
    float: left;
    border: 3px solid #0092BF;
    background-color: #FFEBDD;
    margin-right: 10px;
    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;
    text-align: center;
    cursor: move;
}
.box header {
    color: #fff;
    text-shadow: #000 0 1px;
    box-shadow: 5px;
    padding: 5px;
    background: -moz-linear-gradient(left center, rgb(0,0,0), rgb(79,79,79), rgb(21,21,21));
    background: -webkit-gradient(linear, left top, right top,
                                color-stop(0, rgb(0,0,0)),
                                color-stop(0.50, rgb(79,79,79)),
                                color-stop(1, rgb(21,21,21)));
    border-bottom: 1px solid #ddd;
    -webkit-border-top-left-radius: 5px;
    -moz-border-radius-topleft: 5px;
    border-top-left-radius: 5px;
    -webkit-border-top-right-radius: 5px;
    -moz-border-radius-topright: 5px;
    border-top-right-radius: 5px;
}
</style>
A
B
C

This is obviously just showing the functions available to most browsers with some nice CSS3 effects, the next stage is to add some of the new HTML5 drag events to our mark-up with javaScript, the above example has been adapted from a Google tutorial about Native Dnd which can be seen here and my customised example can be viewed in the Demos section here →

Drag Events

There are a number of different events which are used during the entire drag and drop process, these events are fired to perform different actions that affect the DnD operation:

  • Dragstart- Fired on an element when a drag is started.
  • Dragenter- Fired when the mouse is first moved over an element while a drag is occuring. A listener for this event should indicate whether a drop is allowed over this location. If there are no listeners, or the listeners perform no operations, then a drop is not allowed by default.
  • Drag- This event is fired at the source of the drag, that is, the element where dragstart was fired, during the drag operation.
  • Dragleave- This event is fired when the mouse leaves an element while a drag is occuring. Listeners should remove any highlighting or insertion markers used for drop feedback.
  • Dragover- This event is fired as the mouse is moved over an element when a drag is occuring. Much of the time, the operation that occurs during a listener will be the same as the dragenter event.
  • Drop - The drop event is fired on the element where the drop was occured at the end of the drag operation. A listener would be responsible for retrieving the data being dragged and inserting it at the drop location.
  • Dragend - The source of the drag will receive a dragend event when the drag operation is complete, whether it was successful or not.

3. A Simple Example

DnD requires a few things to start working; something to drag, a drop area and javaScript event handlers on the target to tell the browser it can drop. As mentioned above the <img> and <a>(href) elements are draggable by default.

The example (adapted from HTML5 Doctors Drag and Drop Tutorial) shows how to drag a set of images and display each images information in a box, here’s the HTML mark-up:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset=utf-8 />
<title>Basic Drag and Drop</title>
<style>
    #drop {
        min-height: 100px;
        width: 200px;
        border: 3px dashed #ccc;
        margin: 10px;
        padding: 10px;
    }
    p {
        margin: 3px 0;
    }
</style>
    <script src="http://html5demos.com/js/h5utils.js"></script>
</head>
<body>
    <img src="www.yourimglink.com" alt="Image" />
    <img src="www.yourimglink.com" alt="Image" />
    <img src="www.yourimglink.com" alt="Image" />
    <div id="drop"></div>
</body>
</html>

The included h5utils.js script is a small library built by Remp Sharp that tiggers HTML 5 elements and to give cross browser events binding. Currently the <img> elements, by default, can be dragged but they can’t be dropped.

To drop elements the browser needs to be told that the elements can be dragged over a specific point and then handle the drop event.
To do this, the dragover event needs to be cancelled, and for IE to work the dragenter event needs to be cancelled:

function cancel(e) {
    if (e.preventDefault) {
e.preventDefault();
}
return false;
}
var drop = document.querySelector('#drop');
// Tells the browser that we *can* drop on this target
addEvent(drop, 'dragover', cancel);
addEvent(drop, 'dragenter', cancel);
addEvent(drop, 'drop', function (e) {
    if (e.preventDefault) e.preventDefault(); // stops the browser from redirecting off to the text.

    this.innerHTML += '<p>' + e.dataTransfer.getData('Text') + '</p>';
    return false;
});

Note for this code, it's using addEvent where you’d normally see element.addEventListener, it’s from the h5utils.js library to support IE. The code above firstly looks for the drop target in the DOM using the document.querySelector, then when the user drags the element over another, the dragover event is fired, which then triggers the function called cancel which will prevent the default browser action. The same occurs for dragenter to support IE, then it binds the drop event and acquires the information about what was dropped (text).

Dragover & Dragenter

By cancelling these events, it is telling the browser that this element is over another element and is the one that can be released and dropped upon.

Remy Sharp discusses in his Native HTML5 Drag and Drop Article: I’m still not entirely sure why there’s a difference here, but Firefox and friends needs preventDefault on the event, and IE requires the return false. Note that the examples out in the wild that use inline JavaScript (yuk, bad), the preventDefault is supposed to be implicit, so you won’t see it in the code.

Drop

The drop event is fired on the element where the drop was occured at the end of the drag operation. A listener would be responsible for retrieving the data being dragged and inserting it at the drop location.

So to show what was dropped, the default browsers actions needs to be cancelled, to stop the browser from to a particular location (different site/page) and to get the contents out of the dataTransfer object. The cancelling of the browser default is, again, done using the preventDefault and return false. If we don’t set the dataTransfer data manually the default key is set to Text. So this might be the value of an href of an <a> element, or it might be <img> src address.

The example can be viewed on jsbin here (source)

5. Conclusion

Personally I think that Dnd has huge potential, as the specification develops and resolves issues with the dataTransfer object (currentling having to bind elements independently), it will become much easier to implement. Currently there are some more simple methods using javaScript librarys like JQuery UI, however it's important to note that with Dnd being a native API within the browser, a developer should always take advantage of that.

This is the whole point of HTML5, having a standardised set of browser native, API's at our finger tips! Another important feature is ARIA support so that drag and drop functionality is accessible, a great article about this is over on Dev.Opera by Gez Lemon, so check that out.

The Next Step

The next step with Dnd is to go and develop some amazing demos and try to incoporate them in web apps with ARIA support, including keyboard support. Also the current popular JS libraries (like jQuery UI) that perform Dnd will start to include native HTML5 support will a fall back JS option for users with no browser support.
As this article doesn't reach touch the surface on Dnd, hopefully some of the articles below will provide a wealth of further information: