Tech and Media Labs
This site uses cookies to improve the user experience.




HTML5 Drag and Drop

Jakob Jenkov
Last update: 2014-06-15

From HTML5 it is possible to drag and drop HTML elements inside an HTML page. Via JavaScript event listeners you can decide what happens when the user drags and drops elements.

During drag and drop HTML elements can take on two roles:

  1. Draggable
  2. Drop target / drop zone.

The draggable element is the element which the user should be able to drag around the HTML page and drop somewhere, for some action to occur. This can be one or more elements.

The drop target or drop zone is the element onto which the draggable elements are dropped.

Drag and Drop Events

To control what happens when dragging and dropping HTML elements, you need to perform 3 steps:

  1. Set the HTML elements to draggable.
  2. Attach event listeners to the draggable HTML elements
  3. Attach event listeners to the drop target HTML elements

The events and attributes are illustrated here:

The event listeners on the draggable and drop target elements.

First you set the draggable="true" attribute on the HTML element(s) to make draggable.

Second you attach event listeners on the draggable element for the dragstart and dragend events. Inside these event listeners you can implement what is to happen when the user starts dragging the element, and when the dragging ends.

Third you attach event listeners on the drop target element. The events you can listen for are dragenter, dragover, dragleave and drop.

The dragenter event is fired when the user drags the draggable onto the drop target. This event only fires when the draggable element changes from being outside to being over, which is typically determined by the position of the mouse cursor.

Once the draggable element is over the drop target, the dragover event fires, and keeps firing for as long as the draggable element is being dragged over the drop target.

If the user drags the draggable object out of the drop target again, the dragleave event is fired.

If the the user drops the draggable object on the drop target, the drop event is fired.

Drag and Drop Event Example

Here is an example you can play with. Try to drag the HTML5 logo onto the drop target and see what happens. Try both dropping the image and dragging it out again.

Drop element here
dragstart:
dragend:

dragenter:
dragover:
dragleave:
drop:

Drag and Drop Code

Let us take a look at how to handle the drag and drop events. To see that we will first create an <img> element which we can drag. Here is the HTML code for that:

<img id="draggagle1" src="..." draggable="true">

Once we have a draggable element we need a drop target. I will use a <div> element:

<div id="droptarget1" style="width: 200px; height: 200px;">Drop here</div>

Once we have the draggable element and the drop target element, we need to attach the event listeners. Here is the JavaScript code for that:

<script>
    var draggable = document.getElementById("draggable1");

    draggable.addEventListener('dragstart', dragStart, false);
    draggable.addEventListener('dragend'  , dragEnd  , false);

    var droptarget = document.getElementById("droptarget1");

    droptarget.addEventListener('dragenter', dragEnter  , false);
    droptarget.addEventListener('dragover' , dragOver   , false);
    droptarget.addEventListener('dragleave', dragLeave  , false);
    droptarget.addEventListener('drop'     , drop       , false);


    /* Draggable event handlers */
    function dragStart(event) {
        event.dataTransfer.setData('text/html', "You dragged the image!");
    }

    function dragEnd(event) {
    }

    /* Drop target event handlers */
    function dragEnter(event) {
    }

    function dragOver(event) {
        event.preventDefault();
        return false;
    }

    function dragLeave(event) {
    }

    function drop(event) {
        var data = event.dataTransfer.getData('text/html');
        event.preventDefault();
        return false;
    }
</script>

The dragStart() function calls event.dataTransfer.setData() in order to set the data that is transfered to the drop target when the element is dropped. Whatever data you need to properly finish the drop action, set it here. You set both the data and its mime type.

The dragOver() and drop() event handlers both call event.preventDefault() and return false. This is necessary to make the drag and drop work properly. The browser may have some default drag and drop behaviour which you need to disable to make your code work.

Notice also that the drop() event handler function reads the data set in dragStart() via the call to event.dataTransfer.getData(). It passes the mime type of the data as parameter to properly extract it.

This is all that is needed to implement drag and drop in HTML5. You can make it a bit more visually pleasing though, as we will see in the next section.

Visual Feedback

You can use the drag and drop event handler functions to give the user more explicit visual feedback.

First of all, you can mark the element being dragged so that the user can see which element he or she is dragging. If multiple elements look the same and they can all be dragged, it is nice for the user to see which one is being dragged. You can do so in response to the dragstart event. Here is an example:

var draggable = document.getElementById("draggable1");

draggable.addEventListener('dragstart', dragStart, false);

function dragStart(event) {
    event.dataTransfer.setData('text/html', "You dragged the image!");
    event.target.style.border = "1px solid #cccccc";
}

Once the drag ends, we want to remove the border again, though. This is done in response to the dragend event. Here is how that is done:

var draggable = document.getElementById("draggable1");

draggable.addEventListener('dragstart', dragStart, false);
draggable.addEventListener('dragend'  , dragEnd, false);

function dragStart(event) {
    event.dataTransfer.setData('text/html', "You dragged the image!");
    event.target.style.border = "1px solid #cccccc";
}

function dragEnd(event) {
    event.target.style.border = "none";
}

Now the user will get visual feedback showing which element is being dragged.

We also want to show the user that it is possible to drop the draggable element when it is dragged over the drop target. Again we will change the border of the element. We will do so in response to the dragenter, dragleave and drop events. Here is how that is done:

    var droptarget = document.getElementById("droptarget1");

    droptarget.addEventListener('dragenter', dragEnter  , false);
    droptarget.addEventListener('dragleave', dragLeave  , false);
    droptarget.addEventListener('drop'     , drop       , false);

    /* Drop target event handlers */
    function dragEnter(event) {
        event.target.style.border = "2px dashed #ff0000";
    }

    function dragLeave(event) {
        event.target.style.border = "none";
    }
    function drop(event) {
        event.target.style.border = "none";
        event.preventDefault(); // don't forget this!
    }

Now the border of the drop target will become green and dashed when the draggable element is dragged over it. The border will be removed when the draggable element is dragged out again, or if the element is dropped on the drop target.

The example above did not show the dragOver() event handler function, but make sure that you add it to prevent the default browser behaviour.

Here is the full code after the visual feedback has been added:

<script>
    var draggable = document.getElementById("draggable1");

    draggable.addEventListener('dragstart', dragStart, false);
    draggable.addEventListener('dragend'  , dragEnd  , false);

    var droptarget = document.getElementById("droptarget1");

    droptarget.addEventListener('dragenter', dragEnter  , false);
    droptarget.addEventListener('dragover' , dragOver   , false);
    droptarget.addEventListener('dragleave', dragLeave  , false);
    droptarget.addEventListener('drop'     , drop       , false);


    /* Draggable event handlers */
    function dragStart(event) {
        event.dataTransfer.setData('text/html', "You dragged the image!");
        event.target.style.border = "1px solid #cccccc";
    }

    function dragEnd(event) {
        event.target.style.border = "none";
    }



    /* Drop target event handlers */
    function dragEnter(event) {
        event.target.style.border = "2px dashed #ff0000";
    }

    function dragOver(event) {
        event.preventDefault();
        return false;
    }

    function dragLeave(event) {
        event.target.style.border = "none";
    }

    function drop(event) {
        event.target.style.border = "none";
        var data = event.dataTransfer.getData('text/html');
        event.preventDefault();
        return false;
    }
</script>

The DataTransfer Object, effectsAllowed, dropEffect and setDragImage()

You can increase the visual feedback given to the user during drag and drop actions using the DataTransfer object. The DataTransfer object has 2 attributes and one function you can use for this purpose. These are:

  • effectsAllowed
  • dropEffect
  • setDragImage()

You have access to the DataTransfer object in the dragstart and drop event objects. Here is an example dragstart listener function which sets the effectsAllowed property on the DataTransfer object:

 function dragStart(event) {
    event.dataTransfer.effectsAllowed = "copy";
    event.dataTransfer.setData('text/html', "You dragged the image!");
    event.target.style.border = "1px solid #cccccc";
}

The effectsAllowed property is used by the browsers to change the mouse cursor to show what kind of action is performed when dragging and dropping an element. Typically the mouse cursor changes when the dragged element is over a drop target. Not before. Valid values for the effectsAllowed property are:

  • none
  • copy
  • move
  • copyMove
  • link
  • linkMove
  • copyLink
  • all
  • uninitialized

The dropEffect is supposed to show the user (via the cursor) what happens when the mouse hovers over a drop target, but at the time of writing (feb. 2014) the browsers seem to ignore this. Valid values for the dropEffect property are:

  • none
  • copy
  • link
  • move

The setDragImage(image, x, y) function can be used to set the image shown by the browser when the user drags an element. By default the browser shows a semi-transparent copy of the original element, but if you want a different image, you can set a different image using this function. The x and y properties can be used to set location offsets for the image when displayed. By default the upper left corner of the drag image is located at the tip of the mouse pointer. By setting different x and y properties you can change this. You can use either positive or negative x and y offsets.

Here is a code example showing how to set a drag image inside the dragStart() event listener function:

function dragStart(event) {
    event.dataTransfer.effectAllowed  = "all";
    event.dataTransfer.dropEffect     = "copy";

    
    var dragImage = document.createElement('img');
    dragImage.src = dragImageUrl;
    dragImage.width = 75;
    event.dataTransfer.setDragImage(dragImage, 0, 0);
    
    event.dataTransfer.setData('text/html', "You dragged the image!");
    event.target.style.border = "1px solid #cccccc";
}

Here is an example that lets you play with the various settings on the DataTransfer object. Try changing the effectsAllowed and drag image and see what happens when you drag the HTML5 logo down over the div drop target.




Drop here

Dragging Files Into The Browser

It is possible to drag files into the browser from the file system, and read the name and content of the dragged files from JavaScript. You do so via the HTML5 File API. Here is a drop target listener which detects the file name of the dragged file:


var droptarget2 = document.getElementById("droptarget2");
droptarget2.addEventListener('drop'     , drop       , false);


function drop(event) {

    // Files - array of dragged files.
    var files = event.dataTransfer.files;

    for(var i= 0; i < files.length; i++){
        var file = files[i];
        console.log("file: " + file.name);
    }

    event.preventDefault();
    return false;
}

Notice how the drop() function does not call the getData() function, but instead accesses the files property of the DataTransfer. The files property contains a list of the files that were dragged into the browser. To learn how to read these files, consult the HTML5 File API (I will write about it soon).

You can read more about how to access the dragged files in my HTML5 file API tutorial.

Jakob Jenkov




Copyright  Jenkov Aps
Close TOC