HTML5 Drag and Drop
Jakob Jenkov |
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:
- Draggable
- 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:
- Set the HTML elements to draggable.
- Attach event listeners to the draggable HTML elements
- Attach event listeners to the drop target HTML elements
The events and attributes are illustrated here:
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.
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.
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.
Tweet | |
Jakob Jenkov |