Simple Drag-and-Drop File Upload With JS and HTML5

INTRODUCTION

SIMPLE ELEGANT UPLOAD

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.

 

 

 

PREAMBLE

DOWNLOAD & DEMO

First, here is the download link to the source code as promised and a quick demo.

 

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

  • There is nothing to install, just download and unzip into a folder.
  • Captain Obvious to the rescue – AJAX does not work on file://, use http:// instead.

If you spot a bug, please feel free to comment below. I try to answer 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

Take note, no actual upload will happen here… This demo will only show that drag-and-drop works.

Drop Files Here

 

 

SECTION A

THE HTML & CSS

All right, let us now get started with the HTML and CSS of the drag-and-drop file uploader.

 

HTML LAYOUT

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="upzone">
  Drop Files Here
</div>
 
<!-- (C) UPLOAD STATUS -->
<div id="upstat"></div>
 
<!-- (D) FALLBACK -->
<form id="upform" action="dd-upload.php" method="post" enctype="multipart/form-data">
  <input type="file" name="upfile" accept="image/*" required>
  <input type="submit" value="Upload File">
</form>

Yep, there’s quite a bit going on here.

  • Section A – Self-explanatory… There is Javascript and some CSS involved.
  • Section B – An area to drop the files to start the upload.
  • Section C – Actually optional. Just to show the user on the upload status.
  • Section D – A fallback, traditional HTML upload form for the ancient browsers.

 

THE CSS

dd-upload.css
/* (A) UPLOAD ZONE */
#upzone {
  width: 300px; 
  height: 200px; 
  background: #cfd5ff;
  padding: 10px;
}
#upzone.highlight {
  background: #ff0;
}
 
/* (B) UPLOAD FORM */
#upform {
  display: none;
}

Just some simple cosmetics here for the “dropzone”. Take note – We will add the .highlight CSS class to the dropzone when a file hovers over it, and of course, we hide the fallback form by default.

 

 

SECTION B

THE JAVASCRIPT

With that, let us now move into the “heavyweight main Javascript upload engine”. As this is kind of confusing, I will explain section-by-section instead.

 

INITIALIZATION

dd-upload.js
var ddup = {
  // (A) ON PAGE LOAD
  hzone: null, // HTML upload zone
  hstat: null, // HTML upload status
  hform: null, // HTML upload form
  init : function () {
    // (A1) GET HTML ELEMENTS
    ddup.hzone = document.getElementById("upzone");
    ddup.hstat = document.getElementById("upstat");
    ddup.hform = document.getElementById("upform");
    
    // (A2) DRAG-DROP SUPPORTED
    if (window.File && window.FileReader && window.FileList && window.Blob) {
      // HIGHLIGHT DROPZONE ON FILE HOVER
      ddup.hzone.addEventListener("dragenter", function (e) {
        e.preventDefault();
        e.stopPropagation();
        ddup.hzone.classList.add('highlight');
      });
      ddup.hzone.addEventListener("dragleave", function (e) {
        e.preventDefault();
        e.stopPropagation();
        ddup.hzone.classList.remove('highlight');
      });
      
      // DROP TO UPLOAD FILE
      ddup.hzone.addEventListener("dragover", function (e) {
        e.preventDefault();
        e.stopPropagation();
      });
      ddup.hzone.addEventListener("drop", function (e) {
        e.preventDefault();
        e.stopPropagation();
        ddup.hzone.classList.remove('highlight');
        ddup.queue(e.dataTransfer.files);
      });
    }

    // (A3) DRAG-DROP UPLOAD NOT SUPPORTED
    else {
      ddup.hzone.style.display = "none";
      ddup.hform.style.display = "block";
    }
  }
}
window.addEventListener("DOMContentLoaded", ddup.init);

Not going to explain line-by-line, but in essence:

  • Captain Obvious, the var ddup object contains all the upload mechanics.
  • The init() function will fire on page load.
  • All it does are basically 3 things:
    • Fetch a reference to the HTML elements (drop zone, status, fallback form).
    • Attach a “drop-to-upload” if the drag-and-drop upload is supported.
    • Otherwise, this will hide the dropzone and show the fallback upload form instead.

 

 

UPLOAD QUEUE

dd-upload.js
var ddup = {
  // (B) UPLOAD QUEUE + HANDLER
  // NOTE: AJAX IS ASYNCHRONOUS
  // A QUEUE IS REQUIRED TO STOP SERVER FLOOD
  upqueue : [], // upload queue
  uplock : false, // currently uploading a file
  queue : function (files) {
    // FILE LIST INTO QUEUE
    for (let f of files) {
      // OPTIONAL - SHOW UPLOAD STATUS
      ddup.hstat.innerHTML += `<div>${f.name} - Added to queue</div>`;
      // ADD TO QUEUE
      ddup.upqueue.push(f); 
    }
    // GO!
    ddup.go();
  }
}

Yep, you sharp code ninjas should be wondering – Why don’t we immediately start the upload on drop? Why so dumb and roundabout to create an upload queue first? Well, because we will be using AJAX to upload, and it is asynchronous. If the user drops 100 files, that will upload 100 files in parallel and flood the server.

Nothing too complicated here actually, we are just keeping the dropped files into upqueue and usin the uplock boolean flag to restrict one upload at a time.

 

AJAX UPLOAD

dd-upload.js
var ddup = {
   // (C) AJAX UPLOAD
  go : function () { if (!ddup.uplock && ddup.upqueue.length!=0) {
    // (C1) QUEUE STATUS UPDATE
    ddup.uplock = true;
 
    // (C2) PLUCK OUT FIRST FILE IN QUEUE
    let thisfile = ddup.upqueue[0];
    ddup.upqueue.shift();
    // OPTIONAL - SHOW UPLOAD STATUS
    ddup.hstat.innerHTML += `<div>${thisfile.name} - Upload started</div>`;
 
    // (C3) UPLOAD DATA
    let data = new FormData();
    data.append('upfile', thisfile);
    // ADD MORE POST DATA IF YOU WANT
    // data.append("KEY", "VALUE");
 
    // (C4) AJAX REQUEST
    let xhr = new XMLHttpRequest();
    xhr.open("POST", "dd-upload.php");
    xhr.onload = function () {
      // OPTIONAL - SHOW UPLOAD STATUS
      ddup.hstat.innerHTML += `<div>${thisfile.name} - ${this.response}</div>`;
      // NEXT BETTER PLAYER!
      ddup.uplock = false;
      ddup.go();
    };
    xhr.onerror = function(evt){
      // OPTIONAL - SHOW UPLOAD STATUS
      ddup.hstat.innerHTML += `<div>${thisfile.name} - AJAX ERROR</div>`;
      // NEXT BETTER PLAYER!
      ddup.uplock = false;
      ddup.go();
    };
    xhr.send(data);
  }}
};

Lastly, the 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.

 

 

EXTRA

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";

 

LINKS & REFERENCES

 

CLOSING

WHAT NEXT?

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!

24 thoughts on “Simple Drag-and-Drop File Upload With JS and HTML5”

  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 *