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.effectAllowedhas an initial value of"uninitialized"(Chrome:"all")event.dataTransfer.dropEffecthas an initial value of"move"(Chrome:"none")event.relatedTargetis set ondragenter/dragleavewhen dragging between elementslastModifiedDateofFileobjects is logged as deprecated in favor oflastModifiedevent.dataTransfer.typescontains both"Files"and"application/x-moz-file"event.dataTransfer.items[n].typeis always set to"application/x-moz-file", even ondropwebkitGetAsEntry()returnsFileSystemFileEntry/FileSystemDirectoryEntryinstances
UseisFile/isDirectoryinstead ofinstanceofto check the type (Chrome:FileEntry/DirectoryEntry)
- All drag / drop events are
MouseEventinstances in Safari (Chrome:DragEvent) event.dataTransfer.itemsis not available in Safari
Mime types or number of dragged files can therefore not be determined in drag events, only ondropwebkitGetAsEntry()is not available in Safaridragenteranddragleaveevents always have theirrelatedTargetset tonulllastModifiedDateis not available onFileobjects, butlastModifiedis
event.dataTransfer.typesis aDOMStringList, not a string arrayevent.dataTransfer.dropEffecthas an initial value of"copy"(Chrome:"none")dragenteranddragleaveevents always have theirrelatedTargetset tonulllastModifiedis not available onFileobjects, butlastModifiedDateiswebkitGetAsEntry()returnsWebKitFileEntry/WebKitDirectoryEntryinstances
UseisFile/isDirectoryinstead ofinstanceofto check the type (Chrome:FileEntry/DirectoryEntry)
event.dataTransfer.itemsis not available in Internet Explorer
Mime types or number of dragged files can therefore not be determined in drag events, only ondropevent.relatedTargetis set ondragenter/dragleavewhen dragging between elementswebkitGetAsEntry()is not available in Internet Explorerevent.dataTransfer.typesis aDOMStringList, not a string array- Drop events for folders do not contain directories in
event.dataTransfer.files lastModifiedis not available onFileobjects, butlastModifiedDateis
- 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