Once upon a time, a student downloaded various widgets and frameworks to create a fancy drag and drop file upload… Only to be defeated after a day of fumbling. Well, there’s no need to struggle with libraries, we can build a drag and drop file upload using native Javascript – Let Master Coffee show you how, let’s go.
CODE DOWNLOAD
I have released this under the MIT license, feel free to use it in your own project – Personal or commercial. Some form of credits will be nice though. 🙂
VIDEO TUTORIAL
DRAG & DROP FILE UPLOAD DEMO
Take note that this demo will only showcase “file drag and drop”, no actual upload will happen.
1) THE HTML
<!-- (PART A) FILE DROP ZONE -->
<div id="ddzone">Drop Files Here</div>
<!-- (PART B) FILE UPLOAD QUEUE -->
<div id="ddqueue"></div>
There are only 2 parts to the HTML.
<div id="ddzone">
Drop files here to upload.<div id="ddqueue">
File upload queue.
2) THE JAVASCRIPT – INIT
var ddup = {
// (PART A) PROPERTIES
hZone : null, // html file drop zone
hQueue : null, // html file queue
queue : [], // file upload queue
lock : false, // upload in progress
// (PART B) INIT
init : () => {
// (B1) GET HTML ELEMENTS
ddup.hZone = document.getElementById("ddzone");
ddup.hQueue = document.getElementById("ddqueue");
// (B2) COSMETICS - HIGHLIGHT DROP ZONE
ddup.hZone.ondragenter = () => ddup.hZone.classList.add("hover");
ddup.hZone.ondragleave = () => ddup.hZone.classList.remove("hover");
ddup.hZone.ondragend = () => ddup.hZone.classList.remove("hover");
// (B3) PREVENT DRAG OVER - NECESSARY FOR DROP TO WORK
ddup.hZone.ondragover = e => e.preventDefault();
// (B4) DROP TO QUEUE FILE
ddup.hZone.ondrop = e => {
// (B4-1) PREVENT BROWSER DEFAULT ACTION & REMOVE HIGHLIGHT
e.preventDefault();
ddup.hZone.classList.remove("hover");
// (B4-2) ADD FILES TO QUEUE
for (let file of e.dataTransfer.files) {
let row = document.createElement("div");
row.className = "row";
row.innerHTML = `<div class="fname">${file.name} (${file.size} bytes)</div>
<div class="fprog">0%</div>`;
ddup.hQueue.append(row);
file.hprog = row.querySelector(".fprog");
ddup.queue.push(file);
}
// (B4-3) START UPLOAD
ddup.upload();
};
},
...
};
// (PART D) START
window.addEventListener("load", ddup.init);
- All the mechanics are contained within the
var ddup = {}
object. - (B & D) On window load, we call
ddup.init()
to initialize the “widget”.- (B1) Get the HTML file drop zone and upload queue.
- (B2) Some cosmetics. Highlight the dropzone when the user drags over, remove the highlight when done.
- (B3 & B4) On drop, loop through the files. Create the respective HTML rows, push the files into the
ddup.queue
array, and callddup.upload()
to start the upload.
3) THE JAVASCRIPT – AJAX UPLOAD
// (PART C) AJAX UPLOAD
upload : () => { if (!ddup.lock) {
// (C1) LOCK UPLOAD
ddup.lock = true;
// (C2) UPLOAD FIRST FILE IN QUEUE
let file = ddup.queue[0],
data = new FormData();
data.append("ddup", file);
// (C3) XHR UPLOAD
let xhr = new XMLHttpRequest();
xhr.open("POST", "dummy.php");
// (C4) UPLOAD PROGRESS
xhr.upload.onprogress = e => file.hprog.innerHTML = Math.ceil((e.loaded / e.total) * 100) + "%";
xhr.onload = () => {
// (C4-1) UPLOAD ERROR
if (xhr.status!=200 || xhr.response !="OK") {
console.error(file, xhr.response);
file.hprog.innerHTML = "ERROR";
}
// (C4-2) UPLOAD OK
else { file.hprog.innerHTML = "100%"; }
// (C4-3) NEXT FILE
ddup.queue.shift();
ddup.lock = false;
if (ddup.queue.length>0) { ddup.upload(); }
};
// (C5) PROCESS UPLOAD
xhr.send(data);
}}
- (C1) The first thing we do is to check and set
ddup.lock = true
, this makes sure that only one upload is happening at a time; It prevents a possible crash when the user drops 999 times, creating 999 parallel uploads at once. - (C2) Pluck the first file from the upload queue.
- (C3 & C5) Upload the file using good old
XMLHttpRequest()
. - (C4) Update the progress in the HTML as it uploads.
- (C4-3) When the upload is complete, proceed to the next file. Unlock
ddup.lock = false
and callddup.upload()
recursively.
P.S. For the advanced folks – We not using fetch()
here, because it does not have a upload.onprogress
callback. There is seemingly no “clever and humane” way to track the upload progress using fetch()
.
EXTRA) DUMMY SERVER UPLOAD HANDLER
<?php
move_uploaded_file(
$_FILES["ddup"]["tmp_name"],
$_FILES["ddup"]["name"]
);
echo "OK";
This one is in PHP, but yep, just process the AJAX upload as a “regular file upload”.
THE END – FILE RESTRICTION
That’s all for this short tutorial and sharing. One last bit for those who want to add file size/type restrictions, modify (B4-2):
- File type restriction –
if (file.name.split(".").pop().toLowerCase() != "EXTENSION") { continue; }
- File size restriction –
if (file.size > BYTES) { continue; }
But of course, it is safer to do the file checks on the server-side… Anyone with some “programming powers” can open the developer’s console and easily mess with restrictions.