This is a writeup to describe the differences between browser implementations of the HTML5 drag & drop API.
- Google Chrome 57
- Mozilla Firefox 52
- Microsoft Internet Explorer 11
- Microsoft Edge 38
- Apple Safari 10.0.3
- Opera 44.0
Last updated: 2017-04-14
Browser testing is done using a single self-contained HTML file in this repository. The page basically consists of this html structure:
<body>
<div class="droptarget"></div>
</body>
Files are then dragged into or out of the page and the <div>
.
All tested browsers follow the same event pattern when a file is dragged into or out of the page:
-
User drags into the page:
DragEvent { type: "dragenter", target: <body> }
-
User drags across the page:
DragEvent { type: "dragover", target: <body> }
(repeated every time the mouse cursor is moved)
-
User drags out of the page:
DragEvent { type: "dragenter", target: <body> }
-
User drops on the page:
DragEvent { type: "drop", target: <body> }
-
User drags from the page into an element:
DragEvent { type: "dragenter", target: <div class="droptarget"> } DragEvent { type: "dragleave", target: <body> }
-
User drags from an element to the body element:
DragEvent { type: "dragenter", target: <body> } DragEvent { type: "dragleave", target: <div class="droptarget"> }
Only Internet Explorer and Firefox set a meaningful relatedTarget
on dragenter
and dragleave
events.
This would make the "enter page" / "leave page" logic very simple:
var draggingInPage = false;
body.addEventListener('dragenter', function () {
draggingInPage = true;
});
body.addEventListener('dragleave', function (event) {
draggingInPage = !event.relatedTarget;
});
body.addEventListener('drop', function () {
draggingInPage = false;
});
This does not work in most other browsers, however - counting "number of events received" is necessary:
var eventCounter = 0;
var draggingInPage = false;
body.addEventListener('dragenter', function () {
eventCounter += 1;
draggingInPage = true;
});
body.addEventListener('dragleave', function () {
eventCounter -= 1;
draggingInPage = eventCounter > 0;
});
body.addEventListener('drop', function () {
eventCounter = 0;
draggingInPage = false;
});
To detecting dragging text vs dragging files, different properties of the events dataTransfer
need to be accessed in different Browsers.
In Chrome, Firefox and Safari, the DataTransfer
object at event.dataTransfer
has a types
property which is a string array that will contain "Files"
when files are dragged:
var draggingFiles = false;
function onDragEnter(event) {
draggingFiles = event.dataTransfer.types.indexOf('Files') >= 0;
}
Microsoft Internet Explorer and Edge will provide a DOMStringList
instead of an array,
therefore using Array
methods like includes
or indexOf
is not possible.
For DOMStringList
, using the contains
method yields the same result:
var draggingFiles = false;
function onDragEnter(event) {
draggingFiles = event.dataTransfer.types.contains('Files');
}
All browsers have a event.dataTransfer.files
property, but it is only filled for drop
events
and has a length
of 0 during dragenter
/dragover
/dragleave
(or is null
altogether).
When jQuery is used for adding the event handler, the DataTransfer
object might not be carried over
to the jQuery-created event, but is still accessible via the browser event stored as originalEvent
:
function dragEventHandler(event) {
var dataTransfer = event.dataTransfer || (event.originalEvent && event.originalEvent.dataTransfer);
// ... other code ...
}
$('body').on('dragenter dragover dragleave', dragEventHandler);
Unlike all other tested browsers, Safari uses MouseEvent
instead of DragEvent
.
function handleAllEvents(event) {
if (event instanceof DragEvent) {
handleDragEvent(event); // not called in Safari
} else {
// ...
}
}
Since this is inconsistent for other events as well, using instanceof
over event.type
is generally a bad idea and not recommended.
To prevent dropping of unexpected files during the drag (e.g. text files when images are expected) or to show a message like "Drop here to upload 2 images" vs "Drop here to upload 2 text files", detecting the mime types or at least the amount of dragged files would be useful.
While all browsers provide a list of files and their mime types once the drag & drop operation
is over and the drop
event is dispatched, not all browsers support detecting the mime types
during dragenter
/dragover
/dragleave
.
Chrome and Edge provide the mime types of the dragged files under dataTransfer.items
.
Firefox does provide an array with a length
corresponding to the number of dragged files,
but sets the mime types to "application/x-moz-file"
before the file is actually dropped.
In the example below, Chrome and Edge would set draggedFileTypes
to
["text/plain", "application/javascript"]
, Firefox would set it to
["application/x-moz-file", "application/x-moz-file"]
.
var draggedFileTypes = null;
function handleDragEnter(event) {
draggedFileTypes = [];
for (let i = 0; i < dataTransfer.items.length) {
if (dataTransfer.items[i].kind === 'file') {
draggedFileTypes.push(dataTransfer.items[i].type || 'unknown');
}
}
}
Older versions of Firefox do not provide the mime types during drag events, but provide a mozItemCount
property on the DataTransfer
object to determine how many files are dragged on the page:
var amountOfDraggedFiles = 0;
function handleDragEnter(event) {
amountOfDraggedFiles = event.dataTransfer.mozItemCount; // only works in Firefox
}
Google Chrome allows drag and drop for folders since Chrome 21, Firefox and Edge added support later.
On all supported browsers, DataTransferItem
has an additional webkitGetAsEntry()
method (also on
browsers which are not based on Webkit) which should be used to distinguish file drag operations from
folder drag operations.
webkitGetAsEntry()
is currently provided by Chrome, Firefox, Edge and Safari.
The File
objects provided under event.files
is mostly indistinguishable from a regular file,
but the type
property is set to ""
, which unfortunately is also the fallback mime type
for files without an extension. None of the tested browsers detects the mime type from file contents.
In Chrome and Edge, checking the mime type in event.items
against ""
could be used during
dragenter
/dragover
/dragleave
events to detect folders only if you can assume that your users
do not upload files without an extension (see note about default mime type above).
Firefox reports "application/x-moz-file"
for files and folders alike.
The size
of the File
object is set to a non-consistent value and should not be used
for detecting folders - 4096
in Chrome, 0
in Edge and Firefox, an arbitrary value
in Safari (68 for an empty folder, 238 for a 918KB folder).
Internet Explorer and Safari do not support drag & drop for folders, but do not prevent it.
On all drag/drop events, they contain a "Files"
entry in event.dataTransfer.items
.
For the drop
event, event.dataTransfer.files
contains folders in Safari, in Internet Explorer
.files
is empty if at least 1 folder is dropped, which can be used to detect folders.
function handleDragEvent(event) {
var isFolder = false;
if (event.items) {
for (var i = 0; i < event.items.length; i++) {
var item = event.items[i];
if (item.webkitGetAsEntry) {
var entry = item.webkitGetAsEntry();
isFolder = entry ? entry.isDirectory : false;
}
}
}
// isFolder is set correctly during all events in Chrome & Edge and during drop in Firefox
}
Differences of file drag & drop in the tested browsers, compared to Google Chrome as a sane baseline.
event.dataTransfer.effectAllowed
has an initial value of"uninitialized"
(Chrome:"all"
)event.dataTransfer.dropEffect
has an initial value of"move"
(Chrome:"none"
)event.relatedTarget
is set ondragenter
/dragleave
when dragging between elementslastModifiedDate
ofFile
objects is logged as deprecated in favor oflastModified
event.dataTransfer.types
contains both"Files"
and"application/x-moz-file"
event.dataTransfer.items[n].type
is always set to"application/x-moz-file"
, even ondrop
webkitGetAsEntry()
returnsFileSystemFileEntry
/FileSystemDirectoryEntry
instances
UseisFile
/isDirectory
instead ofinstanceof
to check the type (Chrome:FileEntry
/DirectoryEntry
)
- All drag / drop events are
MouseEvent
instances in Safari (Chrome:DragEvent
) event.dataTransfer.items
is not available in Safari
Mime types or number of dragged files can therefore not be determined in drag events, only ondrop
webkitGetAsEntry()
is not available in Safaridragenter
anddragleave
events always have theirrelatedTarget
set tonull
lastModifiedDate
is not available onFile
objects, butlastModified
is
event.dataTransfer.types
is aDOMStringList
, not a string arrayevent.dataTransfer.dropEffect
has an initial value of"copy"
(Chrome:"none"
)dragenter
anddragleave
events always have theirrelatedTarget
set tonull
lastModified
is not available onFile
objects, butlastModifiedDate
iswebkitGetAsEntry()
returnsWebKitFileEntry
/WebKitDirectoryEntry
instances
UseisFile
/isDirectory
instead ofinstanceof
to check the type (Chrome:FileEntry
/DirectoryEntry
)
event.dataTransfer.items
is not available in Internet Explorer
Mime types or number of dragged files can therefore not be determined in drag events, only ondrop
event.relatedTarget
is set ondragenter
/dragleave
when dragging between elementswebkitGetAsEntry()
is not available in Internet Explorerevent.dataTransfer.types
is aDOMStringList
, not a string array- Drop events for folders do not contain directories in
event.dataTransfer.files
lastModified
is not available onFile
objects, butlastModifiedDate
is
- Since version 15 (7/2013), Opera is based on Chromium and behaves like Google Chrome.
If you find any incorrect or outdated information, I would appreciate a pull request to this repository.
Leon Adler - Test case & this document
Bernhard Riegler - Testing on macOS