Drag-and-Drop File Upload In HTML Javascript (Simple Example)

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

Drop Files Here

Take note that this demo will only showcase “file drag and drop”, no actual upload will happen.

 

 

1) THE HTML

ddupload.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

ddupload.js
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 call ddup.upload() to start the upload.

 

 

3) THE JAVASCRIPT – AJAX UPLOAD

ddupload.js
// (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 call ddup.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

dummy.php
<?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.