Simple Drag-and-Drop File Upload With JS HTML5 (Free Download)

Welcome to a quick tutorial on how to create a simple drag-and-drop file upload with Javascript and HTML. Forget those old boring “select a file” uploads. The Stone Age of the Internet is long over and it is time to create more exciting uploads. A drag-and-drop upload is actually not really that difficult to implement, and we can do it with just pure simple vanilla Javascript. Read on to find out how!

ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

TABLE OF CONTENTS

Download & Demo How It Works Useful Bits & Links
The End

 

 

DOWNLOAD & DEMO

Firstly, here is the download link to the example code as promised.

 

EXAMPLE CODE DOWNLOAD

Click here to download the source code in a zip file – I have released it under the MIT License, so feel free to build on top of it if you want to.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.

 

DRAG-DROP UPLOAD DEMO

dd-upload.html
<!-- (A) CSS + JS -->
<link rel="stylesheet" href="dd-upload.css"/>
<script src="dd-upload.js"></script>
 
<!-- (B) FILE DROP ZONE -->
<div id="demo"></div>
 
<!-- (C) ATTACH -->
<script>
ddup.init({
  target : document.getElementById("demo"), // target html <div>
  action : "dd-upload.php", // server-side upload handler
  data : { key : "value" } // optional, extra post data
});
</script>

For you guys who just want to use this as a “plugin”:

  1. Captain Obvious, load the CSS and Javascript.
  2. Define an empty <div> to generate the uploader.
  3. Use ddup.init() to attach the uploader.
    • target Required, HTML <div> to generate the uploader.
    • action Required, server-side script that will be handling the upload.
    • data Optional, additional data to POST to the server.

P.S. No actual upload will happen here, this demo will only show that drag-and-drop works.

 

 

HOW DRAG-AND-DROP UPLOAD WORKS

All right, let us now get into the details of how the drag-and-drop uploader works.

 

PART A) INITIALIZE UPLOADER

dd-upload.js
// (A) ATTACH DRAG-DROP FILE UPLOADER
init : (instance) => {
  // (A1) FLAGS + CSS CLASS
  instance.target.classList.add("upwrap");
  instance.upqueue = []; // upload queue
  instance.uplock = false; // uploading in progress
 
  // (A2) DRAG-DROP HTML INTERFACE
  instance.target.innerHTML =
    `<div class="updrop">Drop Files Here To Upload.</div>
     <div class="upstat"></div>`;
  instance.hzone = instance.target.querySelector(".updrop");
  instance.hstat = instance.target.querySelector(".upstat");
 
  // (A3) HIGHLIGHT DROP ZONE ON DRAG ENTER
  instance.hzone.ondragenter = (e) => {
    e.preventDefault();
    e.stopPropagation();
    instance.hzone.classList.add("highlight");
  };
  instance.hzone.ondragleave = (e) => {
    e.preventDefault();
    e.stopPropagation();
    instance.hzone.classList.remove("highlight");
  };
 
  // (A4) DROP TO UPLOAD FILE
  instance.hzone.ondragover = (e) => {
    e.preventDefault();
    e.stopPropagation();
  };
  instance.hzone.ondrop = (e) => {
    e.preventDefault();
    e.stopPropagation();
    instance.hzone.classList.remove("highlight");
    ddup.queue(instance, e.dataTransfer.files);
  };
},

You already know this one, we call ddup.init() to attach the uploader to an HTML <div>. A quick walkthrough:

  • (A1) Take extra note of the instance.upqueue and instance.uplock here, these are necessary for the upload queue later.
  • (A2) We generate 2 more sections within the target: <div>.
    • <div class="updrop"> The “dropzone”, drop files here to upload.
    • <div class="upstat"> Upload status.
  • (A3 & A4) Drag-and-drop listeners. Drop a file to add to the queue, start the upload.

 

 

PART B) UPLOAD QUEUE

dd-upload.js
// (B) UPLOAD QUEUE
// * AJAX IS ASYNCHRONOUS, UPLOAD QUEUE PREVENTS SERVER FLOOD
queue : (instance, files) => {
  // (B1) PUSH FILES INTO QUEUE + GENERATE HTML ROW
  for (let f of files) {
    f.hstat = document.createElement("div");
    f.hstat.className = "uprow";
    f.hstat.innerHTML =
      `<div class="upfile">${f.name} (${f.size} bytes)</div>
       <div class="upprog">
         <div class="upbar"></div>
         <div class="uppercent">0%</div>
       </div>`;
    f.hbar = f.hstat.querySelector(".upbar");
    f.hpercent = f.hstat.querySelector(".uppercent");
    instance.hstat.appendChild(f.hstat);
    instance.upqueue.push(f);
  }
 
  // (B2) UPLOAD!
  ddup.go(instance);
},

When files are dropped into the “dropzone”, ddup.queue() will loop through all the files:

  • Create the corresponding HTML file upload status.
  • Push the file into instance.upqueue.

The reason for this “gimmick” is simple – AJAX is asynchronous. It will be bad if the user drops hundreds of files, and the server has to handle hundreds of parallel uploads at once. So instead of a parallel upload, we will loop through instance.upqueue and upload one-by-one instead.

 

 

PART C) AJAX UPLOAD

dd-upload.js
// (C) AJAX UPLOAD
go : (instance) => { if (!instance.uplock && instance.upqueue.length!=0) {
  // (C1) UPLOAD STATUS UPDATE
  instance.uplock = true;
 
  // (C2) PLUCK OUT FIRST FILE IN QUEUE
  let thisfile = instance.upqueue[0];
  instance.upqueue.shift();
 
  // (C3) UPLOAD DATA
  let data = new FormData();
  data.append("upfile", thisfile);
  if (instance.data) { for (let [k, v] of Object.entries(instance.data)) {
    data.append(k, v);
  }}
 
  // (C4) AJAX UPLOAD
  let xhr = new XMLHttpRequest();
  xhr.open("POST", instance.action);
 
  // (C5) UPLOAD PROGRESS
  let percent = 0, width = 0;
  xhr.upload.onloadstart = (evt) => {
    thisfile.hbar.style.width = 0;
    thisfile.hpercent.innerHTML = "0%";
  };
  xhr.upload.onloadend = (evt) => {
    thisfile.hbar.style.width = "100%";
    thisfile.hpercent.innerHTML = "100%";
  };
  xhr.upload.onprogress = (evt) => {
    percent = Math.ceil((evt.loaded / evt.total) * 100) + "%";
    thisfile.hbar.style.width = percent;
    thisfile.hpercent.innerHTML = percent;
  };
 
  // (C6) UPLOAD COMPLETE
  xhr.onload = function () {
    // (C6-1) ERROR
    if (this.response!= "OK" || this.status!=200) {
      thisfile.hpercent.innerHTML = this.response;
    }
    // (C6-2) NEXT BETTER PLAYER!
    else {
      thisfile.hbar.style.width = "100%";
      thisfile.hpercent.innerHTML = "100%";
      instance.uplock = false;
      ddup.go(instance);
    }
  };

  // (C7) GO!
  xhr.send(data);
}}

Lastly, the ddup.go() function is the one that does the actual upload. Nothing special here, just the good old AJAX. Also, remember using the uplock flag to restrict one at a time? Just take a small note on how the lock engages at the start of the function and unlocks when the upload ends.

 

 

USEFUL BITS & LINKS

That’s all for this tutorial, and here is a small section on some extras and links that may be useful to you.

 

HOW ABOUT THE SERVER-SIDE!?

It’s all up to you to handle the upload on the server-side, but if you are using PHP, here is a very simple upload handler:

dd-upload.php
<?php
$source = $_FILES["upfile"]["tmp_name"];
$destination = $_FILES["upfile"]["name"];
move_uploaded_file($source, $destination);
echo "OK";

 

COMPATIBILITY CHECKS

This drag-and-drop AJAX upload tutorial will work on all modern browsers.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end of this tutorial. I hope that it has solved your upload woes, and if you have anything to add to this guide, please feel free to comment below. Good luck and happy coding!

36 thoughts on “Simple Drag-and-Drop File Upload With JS HTML5 (Free Download)”

  1. Do you know why , when I drag a file and drop it, the names of the files do not display in the `drop here box`? Is there something else I need to add here?

    1. As above. In 2-drag-drop-upload.html, the file list will be placed in <div id="upstat"></div>. You are probably on file://, and that’s why the AJAX uploads fail.

  2. Hi WXZHOU,

    Very well presented. I’d like to pass “id=6” from the drag-drop form to simple-upload.php.
    Your comment to modify the run function isn’t quite specific enough for me to implement it. I’ve tried this but it doesn’t work:

    2-drag-drop-upload.js:
    // @TODO – ADD MORE POST DATA IF YOU WANT
    // data.append(“foo”, “bar”);
    var id = data.id;
    xhr.open(‘POST’, ‘simple-upload.php’ + ‘?id=’ + id, true);

    Would you show an actual example of passing a form value to the XMLHttpRequest?

    Thank you, Steve

  3. Hi, great script, I am using it to upload files but need the file modified date sent from the script how can I implement javascript’s lastModified into the script so it will send that to the simple-upload.php file.

    input = document.getElementById(‘file-upload’);
    file = input.files[0];
    new Date(file.lastModified);

    something along the lines of this.
    https://jsbin.com/kibidijewu/1/edit?html,output

  4. Hi, I’m trying to modify the form to add a select file button so it will work on mobile devices. I’ve added an input:

    removed the drag and drop section:

    “window.addEventListener(“load”, function () {
    // IF DRAG-DROP UPLOAD SUPPORTED
    if (window.File && window.FileReader && window.FileList && window.Blob) { ………”

    and added the following:

    window.addEventListener(“change”, function(e) {
    e.preventDefault();
    e.stopPropagation();
    upcontrol.start(e.dataTransfer.files);
    });

    When I select a file I get the following error:

    “Uncaught TypeError: Cannot read property ‘files’ of undefined”

    How do I go about getting it to process the upload. Thanks in advance.

      1. thanks, I managed it in the end:

        I added an input (allowing multiple files):

        and replaced the ‘drag and drop’ section of code with this:

        window.addEventListener(“change”, function(e) {
        e.preventDefault();
        e.stopPropagation();
        upcontrol.start(e.target.files);
        });

        works great on desktop and mobile 🙂

  5. Excellent tutorial and so simple, not complicated by progress bars, instant thumbnails and other complicated stuff.
    I’ve tested it out with my PHP code to resize the images and add the image details to a database and it works great 🙂
    Is there a way to indicate to the user that the last file has finished uploading, or redirect to another page when all the images have uploaded?

    Thanks

  6. Hi! This is an awesome script, something I’ve been looking for for a long time. Thank you so much for sharing it.
    The script I’ve been using for many many years on my site is a normal form with the choose file and submit buttons and I’m planning on replacing my script with yours but am wondering if there’s a way to have it change the file name on upload and provide a link after upload.
    Currently mine changes it to a 10 digit number, coinciding with the UTC time and date I believe, and then provides a link to the file after upload. You can view it here if you’d like, EDITED-OUT.com. Would this be possible with your script? I wish I was better at this, but my abilities have deteriorated over the years.
    Thank you for your help in advance. It is very much appreciated. You should have a donation link on your page.

    1. Hi Larry, just change $destination in simple-upload.php to a timestamp using date(“Ymdhis”)? Pretty much a 1-minute job… Get a freelancer from Fiverr to help you if you are still unsure or have more requirements.

  7. Hi WXZHUO, Do you know if your code can handle dragging email attachments from Outlook? I have got some code that will do this but it will only work with Edge. I know this is probably i big ask but I just wondered if you have any thoughts on that.
    Cheers.

    1. Hi Steve, the interface is between Outlook and the browser. As long as Microsoft decides to not support drag-and-drop from Outlook to Chrome or Firefox, there is absolutely nothing we can do.

  8. Hello. Great job and real easy to implement, for a casual “coder” who knows just enough to create havoc.
    However, i’m hitting a roadblock.
    In “simple-upload.php”, I’m trying to change the destination folder for the uploaded file.
    No matter what I try, nothing seems to work.
    If I leave the code unchanged, it uploads perfectly well… in my PHP folder. ($destination = $_FILES[“file-upload”][“name”];)
    If I try something like “$destination = $_FILES[“../newFolder/”][“name”];”, I get an error.
    What am I doing wrong… or more likely, what don’t I understand?
    Thanks for your help.

    1. 1) PHP puts the uploaded file into a temporary folder, found at $_FILES[“file-upload”][“tmp_name”]
      2) $_FILES[“file-upload”][“name”] contains the actual file name.
      3) So if you want to move the temporary file into a folder of your choice, it should be changed to $destination = “PATH/FOLDER/” . $_FILES[“file-upload”][“name”];
      Done!

      1. Dang!
        I did try to research that info over the web but I guess I didn’t look well enough, or too quickly.
        It is so simple when you know it… and I used to know this stuff, a little.
        Anyways, thanks for the quick response. 🙂

  9. Great example! Simple and easy to understand. However I have one more question. How would I need to change this code to display message when more than one files are uploaded. As of now it only displays the status of one (usually the first) file. Thanks in advance for your response.

    1. Updated the code – Also realized it was bad to do uncontrolled async AJAX upload… Potentially allowing users to upload 100 files at once and crashing the server.

  10. Hi, this is called “simple Drag and Drop File Upload”, but is is a form based upload tutorial, nothing to do with drag and drop. when I click the Advertisment for the drag’n’ drop tut on bottom of this side, I come back to this tutorial. i think there is something messed up…?

Leave a Comment

Your email address will not be published. Required fields are marked *