Drag and Drop

part of The Standard

Sam Thorogood
Dev Channel
4 min readJul 11, 2017

--

In the video 👆, I give a quick run-down on Drag and Drop. I talk through:

  • listening to dragover and drop 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:

  1. type text/plain with the text representation
  2. type text/html with HTML representation
  3. (optional) e.g., if it’s a link or an email address, type text/uri-list
Dragging a link—this is from Recommended drag types on MDN—which includes more information on the different types you’ll expect in drag and drop, although some of it is Mozilla-specific

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:

Google Photos’ drag and drop flow

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. 👨‍💻

--

--