<input>
tag with type="file"
. A friendlier method is to allow the user to drag files onto the page from their desktop. Here's how.
The Basics
HTML has long provided an input tag to handle file uploads. It's solid, it's well understood and well supported by server software. It's also a bit ugly, and can sometimes be difficult for users to use if the file selector windo they're presnted with doesn't point to where the files are, and it's not always obvious where files on the desktop are if you're looking for them in the file system.
Enter Drag'n'Drop - just find your files on the desktop and drag them into the web page. Easy and intuitive.
Drag events
Browsers implement a number of drag-related events: drag, dragend, dragenter, dragleave, dragover, dragstart, dragdrop. Some of these are only relevant when handlig drag actions within a page. We'll be concerning ourselves with dragenter, dragleave, dragover and drop.
- dragenter - fired when a dragged item is dragged into a drop target
- dragover - fired whenever a dragged item is dragged within a drop target
- dragleave - fired when a dragged item leaves a drop target
- drop - fired when an item is dropped on a drop target
Of these, dragenter
, dragover
and dragleave
are used for cosmetic purposes. The really interesting item is drop
Setting up the drop target
Nothing complex here - just some basic HTML and some CSS to make it pretty: <style>
#dropTarget {
margin:100px auto;
width:300px;
height:300px;
border:5px dashed grey;
border-radius: 20px;
text-align:center;
line-height:300px;
}
#dropText {
font-family: "Segoe UI",Arial,sans-serif;
font-size:24pt;
margin:0 auto;
}
.highlight {
background-color:lightgray;
}
</style>
<div id="dropTarget">
<p id="dropText">Drop File Here</p>
</div>
Add the event handlers
Most elements on a page are not suitable as drop targets. Thus the default actions for dragenter
and dragover
are to prevent a drop on that element. To allow a drop on an element the event handlers must prevent the default action on those events. This is achieved either by
return false;
or by
event.preventDefault()
The first three event handlers act to provide some visual confirmation that a dragged item is over the target. They do this by adding the highlight
class to the target element. For dragenter
and dragover
we also call preventDefault()
to allow the drop on this element.
dropTarget.addEventListener('dragenter', (e)=>{
dropTarget.classList.add('highlight')
e.preventDefault();
});
dropTarget.addEventListener('dragover', (e)=>{
dropTarget.classList.add('highlight')
e.preventDefault();
});
dropTarget.addEventListener('dragleave', (e)=>{
dropTarget.classList.remove('highlight')
});
The drop
event handler is where the magic happens. The event object contains a list of the dropped files in the detail
property. The handler creates a new FormData
object and appends the files to it. Finally, the FormData
object is posted to the server using fetch
.
dropTarget.addEventListener('drop', (e)=>{
e.preventDefault(); // Stop the browser doing what it might normally do.
let formData = new FormData();
let fileList = e.dataTransfer.files;
for (let i = 0; i < fileList.length; i++) {
formData.append('files[]', fileList[i]);
}
fetch('uploadDemo.php',{
method:"POST",
body: formData
}
).then((resp)=> {
if (resp.ok) {
alert("Files uploaded successfully");
} else {
alert("Files uploaded successfully");
}
});
});
Handling the upload at the server
Posting the files as part of a FormData object delivers the files to the server exactly as if they'd been selected from an HTML form. The server can use its usual method to unpack the files. In the case of PHP this involves unpacking the $_FILES
superglobal array.
Try it!
Drag some files on to the target below. The endpoint should return a list of files and filesizes that are displayed below.
Drop File Here
The download includes a single page demo including all the CHTML, CSS, and Javascript. Also included is a PHP endpoint for your server.
Questions or comments? Contact me!