How to distinguish if a file or folder is being dragged prior to it being dropped?
TL;DR you can't.
If you're wondering why this question still hasn't got an accepted answer, you can read this meta question created by OP, and my answer.
File drag
/drop
in HTML5
I made some research in different pieces of documentation for this topic and tested it by myself on various browsers, so I decided to summarize all I know about drag and drop of files here.
Dragging
When you drag a file you can use some listeners, such as:
dragenter
dragover
dragend
dragleave
Given that these are drag
events, the files
property of event.dataTransfer
will either have length == 0
or be empty (null
).
You can't read files details in a drag event and you can't check if they are folders. This is not a bug, it's a security feature.
Imagine you could read files on a drag event: you would be able to read everything even if the user doesn't want to upload files to your site. It would make no sense, seriously. Imagine you are dragging a file from your desktop to another folder and you accidentally drag it through a web page: now the web page reads your file and stores your personal information on its server... that would be a huge security flaw.
However, you will still be able to detect whether the user is dragging files (and by files I mean folders too, because folders are files) or not by iterating over the array event.dataTransfer.types
. You can create a function that checks if the drag event contains files, and then call it in the event handler.
Example:
function containsFiles(event) {
if (event.dataTransfer.types) {
for (var i=0; i<event.dataTransfer.types.length; i++) {
if (event.dataTransfer.types[i] == "Files") {
return true;
}
}
}
return false;
}
function handleDragEnter(e) {
e.preventDefault();
if (containsFiles(e)) {
// The drag event contains files
// Do something
} else {
// The drag event doesn't contain files
// Do something else
}
}
Dropping
When you drop a file into the drop <div>
(or whatever element you're using as dropzone), you will use a listener for the event drop
to read some file properties such as name, size, type and last modification date.
To detect if a file is a folder, you are going to:
- Check if the file has
type == ""
, because folders have no type. - Check if the file size is a multiple of 4096:
size%4096 == 0
, because folders always have a size multiple of 4096 bytes (which is 4KiB).
Example:
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
var files = e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) { // iterate in the files dropped
if (!f.type && f.size%4096 == 0) {
// The file is a folder
// Do something
} else {
// The file is not a folder
// Do something else
}
}
}
KNOWN ISSUE: Since that folders are actually files, this is the only way to distinguish them from another kind of file. Although this method doesn't give you absolute certainty that a file is a folder: it might be a file without extension and with a size of 0 or exactly N x 4096B.
Working examples
Here are some working examples to see what I said above in action and test it by yourself. Before running them, make sure that your browser supports drag and drop features. Have fun:
- File drop display info (made by me)
- File/folder recognize (made by me)
- File drag detect (from css-tricks)
This is work on Dropping -on drop event- (please note that this doesn't work on dragover event):
isDraggedItemIsFile = function(e) {
// handle FF
if (e.originalEvent.dataTransfer.files.length == 0) {
return false;
}
// handle Chrome
if (e.originalEvent.dataTransfer.items) {
if (typeof (e.originalEvent.dataTransfer.items[0].webkitGetAsEntry) == "function") {
return e.originalEvent.dataTransfer.items[0].webkitGetAsEntry().isFile;
} else if (typeof (e.originalEvent.dataTransfer.items[0].getAsEntry) == "function") {
return e.originalEvent.dataTransfer.items[0].getAsEntry().isFile;
}
}
return true;
};
$forms.on('drop', function(e) {
if (isDraggedItemIsFile(e)) {
// do something if file
} else{
// is directory
}
});
Tested on FF V49, Chrome V55, Edge V25
I was able to get the entire Mimetype of the thing being dragged over my page. Mimetype appears to be blank for folders, so maybe you can distinguish it that way.
Partial code (extracted from React):
function handleDragOver(ev: DragEvent) {
ev.preventDefault();
ev.dataTransfer!.dropEffect = 'copy';
console.log(Array.from(ev.dataTransfer.items).map(i => [i.kind,i.type].join('|')).join(', '));
}
document.addEventListener('dragover',handleDragOver);
Output looks like:
file|image/x-icon, file|image/jpeg, file|application/vnd.ms-excel
When I drag 3 files over my page.
Not sure if it only works on localhost, I haven't uploaded this anywhere yet, but it's totally working.
MDN docs on DataTransferItem
You can separate files from folders by using FileReader or with webkitGetAsEntry()
The webkitGetAsEntry() is not supported by ie11, so keep that in mind!
The code will look like:
onDrop(event) {
let files = event.dataTransfer ? event.dataTransfer.files : 'null';
for(let i = 0, file; file = files[i]; i++) {
var reader = new FileReader();
reader.onload = function (e) {
console.log('it is a file!');
};
reader.onerror = function (e) {
console.log('it is a folder!');
};
reader.readAsText(file);
}
}