How to use the Drag and Drop API
Discover how to use the Drag and Drop API to create interactive experiences for your users
Using the Drag and Drop API you can define which elements in the page are draggable, and intercept when the user drags an element.
It’s very well supported on modern browsers:
Before we start studying the API, we must know how to define which elements in the page are draggable. We can do so by adding the draggable
attribute in the page HTML, with a value of true
:
<div draggable="true">...</div>
This is enough to make the element draggable.
Tip: images, text selections and links are draggable by default unless you set
draggable
to false on them
We can also drag files from the user computer inside the browser. In this case we are transferring files.
Where can we drag elements to is another point we need to clarify. As well as we can’t just drag any element, we can’t also just drop into any element. The element must be valid drop target.
To make an element a drop target you listen for its dragover
event and you either return false from it, or you call preventDefault()
on the event passed:
const element = document.querySelector('#my-drop-target)
element.addEventListener('dragover', event => {
event.preventDefault()
})
Once we do so, we have a draggable element and a drop target, and we can start. The events we can interact with on the draggable element are:
dragstart
drag
dragend
On the drop target:
dragenter
dragover
dragleave
drop
Overview of a drag and drop operation and the events fired
When the user starts dragging a draggable element, clicking on it with the mouse and moving the mouse, or also tapping and keeping the tap and then moving the selection, the dragstart
event is fired on it:
element.addEventListener('dragstart', (event) => {
//...
})
The event
object passed as parameter to the event handling function is a DragEvent
object.
It extends from the more general Event object, shared with all the other events: mouse, keyboard, scrolling, etc.
At this point, the element is being dragged, and the drag
event is fired. Multiple times as the item is being dragged, so we must use throttling like for the scroll
or mouseover
events.
As soon as we enter a drop target:
- the
dragenter
event is fired on the drop target - the
dragover
event is fired on the drop target
If a dragged element first enters a drop target and then moves away from it, the dragleave
event is fired on the drop target.
If the user releases the mouse, the dragend
event is fired on the element that is being dragged, and a drop
event is fired on the drop target.
Dragging data: DataTransfer
Every event related to drag and drop comes is a DragEvent
object as I mentioned, and it comes with a property called dataTransfer
which holds the data being dragged, and offers 5 properties:
dropEffect
effectAllowed
files
items
(read only)types
(read only)
When the drag event starts, you can do a few operations.
Set / get the effect
You can set the desired effect of the drag operation by setting the effectAllowed
property in the dragstart
event. You have a few options which set how the drop target should handle the dropped element:
none
it shouldn’t be droppedmove
it can be movedcopy
it can be copiedlink
it can be linkedcopyMove
it can be copied or movedcopyLink
it can be copied or linkedlinkMove
it can be moved or linkedall
it can be copied, moved or linked
(all are strings).
The default is all
.
The dropEffect
property is used to get the type of the drag and drop operation, which this time is set by the user through the use of modifier keys. For example, on a Mac pressing the Alt
key sets the drop target to copy the item instead of moving it.
This property is not read only. We can edit it in a dragenter
or dragover
event, to one of those string values:
none
it shouldn’t be droppedmove
it can be movedcopy
it can be copiedlink
it can be linked
Example:
element.addEventListener('dragenter', (event) => {
event.dataTrasfer.dropEffect = 'move'
})
The data being transferred
You can access the items being transferred from the dataTransfer.items
property, which is an array-like object which you can iterate using a loop and get access to each DataTransferItem
object.
DataTransferItem
has 2 read-only properties:
kind
: the kind of the item being dragged. Returns a string containingfile
orstring
type
the MIME type of the item
and it has 2 methods:
getAsFile()
returns aFile
object representing the data being draggedgetAsString()
executes the callback function pasing a string object representing the data being dragged
They have a similar name but work very differently. The first returns a File
object:
element.addEventListener('dragenter', (event) => {
for (item of event.dataTrasfer.items) {
const theFile = item.getAsFile()
}
})
Learn more about File objects at https://flaviocopes.com/file/
The second passes the item as a string to a callback function:
element.addEventListener('dragenter', (event) => {
for (item of event.dataTrasfer.items) {
item.getAsString((theString) => {
console.log(theString)
})
}
})
The types of items files being dragged is stored in the types
property of the dataTransfer
object. It is an array containing the string string
by default. If we are dragging a file, the corresponding type is a string of value Files
.
If there are files being transferred, in addition to being listed in dataTransfer.items
, they are stored in the files
property of dataTransfer
.
This property points to a FileList
object listing the files being dragged.
Check out this demo on Codepen: https://codepen.io/flaviocopes/pen/JqXZoB/
→ I wrote 17 books to help you become a better developer, download them all at $0 cost by joining my newsletter
→ JOIN MY CODING BOOTCAMP, an amazing cohort course that will be a huge step up in your coding career - covering React, Next.js - next edition February 2025