How to Use HTML5 File Drag and Drop

Share this article

Dragging and dropping files from your desktop to a browser is one of the ultimate goals for web application integration. This is the first in a four-part series of posts which describes how to:

  1. enable file dragging and dropping onto a web page element
  2. analyze dropped files in JavaScript
  3. load and parse files on the client
  4. asynchronously upload files to the server using XMLHttpRequest2
  5. show a graphical progress bar while the upload occurs
  6. use progressive enhancement to ensure your file upload form works in any browser (good news for all you IE6 fans!)
  7. code it in plain ol’ JavaScript without a library.
Phew.

Big, Bad Browser Support

Before we begin, this tutorial refers to several cutting-edge HTML5 techniques so expect support to be patchy. The code works today, but it’s possible the APIs will change and browsers will evolve.
  • Recent versions of Firefox and Chrome support all features and work perfectly.
  • Opera can parse files in JavaScript, but file dropping and XMLHttpRequest2 uploading is not implemented.
  • IE and the desktop editions of Safari do not support any of the APIs.
  • Apple has disabled HTML file upload forms on the iPhone and iPad editions of Safari. Anyone know why?
Finally, note that my code shows the fundamental concepts. There’s little error checking and you would need to adapt it for a production system.

The HTML and CSS

Here’s our standard form with a file input type. The only HTML5 feature is the “multiple” attribute which allows the user to select any number of files. We’ll be uploading files to a server running PHP but the code is much the same no matter what technology you’re using. The hidden MAX_FILE_SIZE value specifies 300,000 bytes — this is used by PHP but we’ll also check it client-side to prevent huge file uploads.

<form id="upload" action="upload.php" method="POST" enctype="multipart/form-data">

<fieldset>
<legend>HTML File Upload</legend>

<input type="hidden" id="MAX_FILE_SIZE" name="MAX_FILE_SIZE" value="300000" />

<div>
	<label for="fileselect">Files to upload:</label>
	<input type="file" id="fileselect" name="fileselect[]" multiple="multiple" />
	<div id="filedrag">or drop files here</div>
</div>

<div id="submitbutton">
	<button type="submit">Upload Files</button>
</div>

</fieldset>

</form>

<div id="messages">
<p>Status Messages</p>
</div>
The #filedrag element will be used as our file drag and drop location. The element is hidden in CSS but it will be enabled in JavaScript if drag and drop is supported:

#filedrag
{
	display: none;
	font-weight: bold;
	text-align: center;
	padding: 1em 0;
	margin: 1em 0;
	color: #555;
	border: 2px dashed #555;
	border-radius: 7px;
	cursor: default;
}

#filedrag.hover
{
	color: #f00;
	border-color: #f00;
	border-style: solid;
	box-shadow: inset 0 3px 4px #888;
}
We’ve also defined a .hover class which changes the style when the user has dragged a file on to the element. Browsers don’t apply a :hover style in that situation, but we can add the class with JavaScript when the event occurs.

The File API

The W3C File API provides several objects. We’ll be using:
  • FileList: represents an array of selected files.
  • File: represents an individual file.
  • FileReader: an interface which allows us to read file data on the client and use it within JavaScript.

Attaching JavaScript Events

Time to get our hands dirty with some JavaScript. We’re not using a JavaScript library so, to save our typing fingers, we’ll create a couple of helper functions to return an element by ID and output status messages:

// getElementById
function $id(id) {
	return document.getElementById(id);
}

//
// output information
function Output(msg) {
	var m = $id("messages");
	m.innerHTML = msg + m.innerHTML;
}
We’ll now check if the File API is available and call an Init() function:

// call initialization file
if (window.File && window.FileList && window.FileReader) {
	Init();
}

//
// initialize
function Init() {

	var fileselect = $id("fileselect"),
		filedrag = $id("filedrag"),
		submitbutton = $id("submitbutton");

	// file select
	fileselect.addEventListener("change", FileSelectHandler, false);

	// is XHR2 available?
	var xhr = new XMLHttpRequest();
	if (xhr.upload) {
	
		// file drop
		filedrag.addEventListener("dragover", FileDragHover, false);
		filedrag.addEventListener("dragleave", FileDragHover, false);
		filedrag.addEventListener("drop", FileSelectHandler, false);
		filedrag.style.display = "block";
		
		// remove submit button
		submitbutton.style.display = "none";
	}

}
The Init() function:
  1. Sets a “change” event listener to the file input element.
  2. Displays the #filedrag element.
  3. Sets “dragover” and “dragleave” event listeners to change the style of the #filedrag element.
  4. Sets a “drop” event listener for the #filedrag element.
  5. Hides the form submit button — it’s not required since we’ll be analyzing and uploading files as they’re chosen.
Optionally, you could hide the file input element when file dragging is supported. Personally, I prefer to offer both options since dragging and dropping incurs a number of usability issues. The XMLHttpRequest.upload method check prevents problems in Opera. The browser supports File, FileList and FileReader, but not drag and drop events or XMLHttpRequest2. It can therefore display file information but we don’t want to show the #filedrag element or remove the submit button.

File Drop Style Change

Few people have experienced file drag and drop in a web browser. In fact, experienced web users may not consider it to be impossible. Therefore, we’ve used an element which states “drop files here”. We also want to indicate when a file has been dragged onto the #filedrag location by changing it’s styling:

// file drag hover
function FileDragHover(e) {
	e.stopPropagation();
	e.preventDefault();
	e.target.className = (e.type == "dragover" ? "hover" : "");
}

Analyzing Dropped or Selected Files

We’re using the same FileSelectHandler() function regardless of whether one or more files was selected using “Browse” or dragged onto the #filedrag location:

// file selection
function FileSelectHandler(e) {

	// cancel event and hover styling
	FileDragHover(e);

	// fetch FileList object
	var files = e.target.files || e.dataTransfer.files;

	// process all File objects
	for (var i = 0, f; f = files[i]; i++) {
		ParseFile(f);
	}

}
The function:
  1. Calls FileDragHover() to remove hover styling and cancel browser events. This is essential otherwise the browser may attempt to display the file.
  2. Fetches a FileList object. This will either be from the file input box (e.target.files) or #filedrag element (e.dataTransfer.files).
  3. Finally, the function loops through all File objects in the FileList and passes it as an argument to the ParseFile() function…

function ParseFile(file) {

	Output(
		"<p>File information: <strong>" + file.name +
		"</strong> type: <strong>" + file.type +
		"</strong> size: <strong>" + file.size +
		"</strong> bytes</p>"
	);
	
}
The function outputs information using the three main read-only properties provided by the File object:
  • .name: the file name (it does not include path information)
  • .type: the MIME type, e.g. image/jpeg, text/plain, etc.
  • .size: the file size in bytes.
Please view the demonstration page in Firefox, Chrome, or Opera (no drag & drop support). You can also download the files to examine the code. We’ve covered a lot of ground. In my next article, we’ll discover How to Open Dropped Files Using HTML5 and JavaScript This article has also been translated into Armenian If you enjoyed reading this post, you’ll love Learnable; the place to learn fresh skills and techniques from the masters. Members get instant access to all of SitePoint’s ebooks and interactive online courses, like HTML5 & CSS3 For the Real World. Comments on this article are closed. Have a question about HTML5? Why not ask it on our forums?

Frequently Asked Questions (FAQs) about HTML5 File Drag and Drop

How can I prevent the browser from opening the file when it’s dropped?

To prevent the browser from opening the file when it’s dropped, you need to prevent the default behavior of the ‘drop’ event. You can do this by calling the ‘preventDefault’ method of the event object in your ‘drop’ event handler. Here’s an example:

dropZone.addEventListener('drop', function(e) {
e.preventDefault();
// your code here
});
This will stop the browser from opening the file, allowing you to handle the file drop in your own way.

Can I drag and drop multiple files at once?

Yes, you can drag and drop multiple files at once. The ‘dataTransfer’ object of the ‘drop’ event contains a ‘files’ property, which is a FileList object representing the files being dragged. You can loop through this FileList to handle each file individually. Here’s an example:

dropZone.addEventListener('drop', function(e) {
e.preventDefault();
var files = e.dataTransfer.files;
for (var i = 0; i < files.length; i++) {
// handle each file
}
});
This will allow you to handle multiple files being dropped at the same time.

How can I show a preview of the dropped file?

To show a preview of the dropped file, you can use the FileReader API. This API allows you to read the contents of files (or raw data buffers) stored on the user’s computer. Here’s an example of how you can use it to display a preview of an image file:

var reader = new FileReader();
reader.onload = function(e) {
var img = document.createElement('img');
img.src = e.target.result;
document.body.appendChild(img);
};
reader.readAsDataURL(file);
This will create a new image element and set its source to the data URL of the dropped file, effectively showing a preview of the image.

Can I drag and drop files from one browser window to another?

No, due to security reasons, you cannot drag and drop files from one browser window to another. The drag and drop API only allows you to drag files from your computer into the browser, not between different browser windows or tabs.

How can I handle different types of files differently?

You can handle different types of files differently by checking the ‘type’ property of the File objects in the ‘files’ property of the ‘dataTransfer’ object. This property returns a string representing the MIME type of the file. Here’s an example:

var files = e.dataTransfer.files;
for (var i = 0; i < files.length; i++) {
if (files[i].type.startsWith('image/')) {
// handle image file
} else if (files[i].type.startsWith('text/')) {
// handle text file
}
}
This will allow you to handle image and text files differently.

How can I get the size of the dropped file?

You can get the size of the dropped file by checking the ‘size’ property of the File object. This property returns the size of the file in bytes. Here’s an example:

var file = e.dataTransfer.files[0];
console.log('File size: ' + file.size + ' bytes');
This will log the size of the dropped file to the console.

Can I drag and drop folders?

Yes, you can drag and drop folders. However, handling folders is a bit more complex than handling files, as you need to recursively read the contents of the folders. This can be done using the ‘webkitGetAsEntry’ method of the DataTransferItem interface, which returns a FileSystemEntry object representing the dragged item. You can then use the ‘createReader’ method of the FileSystemDirectoryEntry interface to read the contents of the directory.

How can I restrict the types of files that can be dropped?

You can restrict the types of files that can be dropped by checking the ‘type’ property of the File objects in the ‘files’ property of the ‘dataTransfer’ object, and ignoring files of unwanted types. Here’s an example:

var files = e.dataTransfer.files;
for (var i = 0; i < files.length; i++) {
if (!files[i].type.startsWith('image/')) {
console.log('Ignoring non-image file: ' + files[i].name);
continue;
}
// handle image file
}
This will ignore any dropped files that are not images.

Can I drag and drop text or HTML elements?

Yes, you can drag and drop text or HTML elements. However, this requires a different approach than dragging and dropping files. You need to set the ‘draggable’ attribute of the element to ‘true’, and use the ‘dragstart’ event to set the data to be transferred. Here’s an example:

var draggableElement = document.getElementById('draggable');
draggableElement.setAttribute('draggable', 'true');
draggableElement.addEventListener('dragstart', function(e) {
e.dataTransfer.setData('text/plain', 'This is some text');
});
This will allow you to drag the ‘draggable’ element and drop it into a drop zone, transferring the text ‘This is some text’.

How can I style the drop zone when a file is being dragged over it?

You can style the drop zone when a file is being dragged over it by using the ‘dragenter’ and ‘dragleave’ events to add and remove a CSS class. Here’s an example:

dropZone.addEventListener('dragenter', function(e) {
dropZone.classList.add('over');
});
dropZone.addEventListener('dragleave', function(e) {
dropZone.classList.remove('over');
});
This will add the ‘over’ class to the drop zone when a file is being dragged over it, and remove it when the file leaves the drop zone. You can then use CSS to style the drop zone accordingly.

Craig BucklerCraig Buckler
View Author

Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.

ajaxapiCSS3drag and dropfileHTML5 Tutorials & ArticlesJavaScriptPHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week