Drag and Drop
part of The Standard
In the video 👆, I give a quick run-down on Drag and Drop. I talk through:
- listening to
dragover
anddrop
to disable the default behavior 🚫 - using the
drop
event to replace the contents of an<input type="file">
- using the
DataTransfer
object to interact with dropped files directly - and, most importantly: I advocate using the whole page as a drop target
This might be all you need—it’s the most common use case for Drag and Drop, and the flow that many websites could improve. 🤔
In this article, I go over a few more use cases—including how to respond to non-file content, but also when you drag content out of or within your site.
Dropping indeterminate data
If your users drag files onto your page, it’s simple to disable the default ‘open this here’ behavior— just include two handlers, for dragover
and drop
. Both should call event.preventDefault()
, but drop
can do more.
document.addEventListener('dragover', function(ev) {
ev.preventDefault();
});
document.addEventListener('drop', function(ev) {
ev.preventDefault();
// TODO: do something with ev.dataTransfer.files or .items
});
The DataTransfer
object (read about it on MDN) contains two properties: files
and items
. I cover the files case pretty well in the video.
Items are a list of strings, identified by type. For example, if you drag and drop from a text editor, you’ll get a single string of type text/plain
. If you drag a string from a selection on a webpage, you’ll get a number of strings:
- type
text/plain
with the text representation - type
text/html
with HTML representation - (optional) e.g., if it’s a link or an email address, type
text/uri-list
Show satisfying signs
One thing I don’t cover in the video is showing visual feedback on how a user’s drag and drop operation is going.
This is quite important. My main premise ✍️ is that sites dealing with files that don’t support drag and drop, or that only support it with a tiny drop target, are actually quite confusing. Avoid confronting users with a unknown — they should always know what will happen when a file is dropped.
To show feedback, you can add some behavior in the dragover
event. This even enables you to check the what’s being dragged:
document.addEventListener('dragover', function(ev) {
if (ev.dataTransfer.types.indexOf('Files') !== -1) {
// TODO: show feedback for file dragging
document.classList.add('dragging');
}
});
⚠️ Note that dragover
is run repeatedly while dragging—on Chrome, about every 50ms—so don’t do anything intensive here. You could also respond to the dragenter
event, which only fires once.
To clear the feedback when a user is done, you’ll need to set up a handler on dragleave
(user cancel or mouse leaving) and drop
(success):
['dragleave', 'drop'].forEach(function(eventName) {
document.addEventListener(eventName, function() {
// TODO: cleanup feedback for file dragging
document.classList.remove('dragging');
});
});
I’ll give a shout out to Google Photos, who do a great job at displaying visual feedback while dragging, and use the whole page as a drop target:
Drag your DOM-derived document notes
It is less common but still useful to allow users to natively drag parts of your site. (To be clear, there are plenty of libraries that provide drag and drop entirely in JS—but they’re not done with HTML standards.)
I’ll give a very brief overview below. If you want a more in-depth explanation, then be sure to read Eric Bidelman’s article—from 2010 🗓️—which is still entirely applicable today (and that’s the benefit of standards).
1. Firstly, set draggable
on the element you’d like to make draggable—
const element = document.createElement('div');
element.textContent = 'you can drag me!';
element.className = 'draggable'; // give the user a hint
element.draggable = true; // or...
element.setAttribute('draggable', '');
document.body.appendChild(element);
2. And you’re done! Well—only technically. The element you’ve just created will be natively draggable, but it won’t actually contain any data—so when it’s dropped, its DataTransfer
object is empty. Add a dragstart
handler:
element.addEventListener('dragstart', function(event) {
const dt = event.dataTransfer;
dt.setData('text/plain', 'and I contain some data');
});
You’re not able to specify an actual File
object—not in a standard way, anyway. Unfortunately, as of writing, this means that dragging an actual file from your browser isn’t possible. ❌
3. Be sure to set up dragover
and drop
handlers, just as you would for accepting files passed from the operating system.
Finally, remember that you can specify arbitrary data types that will be passed between different tabs—which could be useful if you’re using drag and drop within sites you control (or where you agree on the format). But be sure to stick to known types text/plain
or text/html
if you’d like to support dragging out to the user’s operating system.
Summary
Drag and Drop is important to get right, especially if you deal with files in any way! If you don’t use the whole page for a drop target 🎯— and users are burned 🚒 by the awkward flow of replacing their context with the dragged file — they’ll learn to never drag files to your site again.
And if you’d just like to play with the Drag and Drop feature for yourself, check out the small demo I built for this article! 🎢
Do you have questions?
Please leave your comments and queries in Medium, or contact me on Twitter. I’m always happy to hear your suggestions or improvements. 👨💻