drag drop files into standard html file input

273,518

Solution 1

The following works in Chrome and FF, but i've yet to find a solution that covers IE10+ as well:

// dragover and dragenter events need to have 'preventDefault' called
// in order for the 'drop' event to register. 
// See: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_operations#droptargets
dropContainer.ondragover = dropContainer.ondragenter = function(evt) {
  evt.preventDefault();
};

dropContainer.ondrop = function(evt) {
  // pretty simple -- but not for IE :(
  fileInput.files = evt.dataTransfer.files;

  // If you want to use some of the dropped files
  const dT = new DataTransfer();
  dT.items.add(evt.dataTransfer.files[0]);
  dT.items.add(evt.dataTransfer.files[3]);
  fileInput.files = dT.files;

  evt.preventDefault();
};
<!DOCTYPE html>
<html>
<body>
<div id="dropContainer" style="border:1px solid black;height:100px;">
   Drop Here
</div>
  Should update here:
  <input type="file" id="fileInput" />
</body>
</html>

You'll probably want to use addEventListener or jQuery (etc.) to register your evt handlers - this is just for brevity's sake.

Solution 2

I made a solution for this.

$(function () {
    var dropZoneId = "drop-zone";
    var buttonId = "clickHere";
    var mouseOverClass = "mouse-over";

    var dropZone = $("#" + dropZoneId);
    var ooleft = dropZone.offset().left;
    var ooright = dropZone.outerWidth() + ooleft;
    var ootop = dropZone.offset().top;
    var oobottom = dropZone.outerHeight() + ootop;
    var inputFile = dropZone.find("input");
    document.getElementById(dropZoneId).addEventListener("dragover", function (e) {
        e.preventDefault();
        e.stopPropagation();
        dropZone.addClass(mouseOverClass);
        var x = e.pageX;
        var y = e.pageY;

        if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {
            inputFile.offset({ top: y - 15, left: x - 100 });
        } else {
            inputFile.offset({ top: -400, left: -400 });
        }

    }, true);

    if (buttonId != "") {
        var clickZone = $("#" + buttonId);

        var oleft = clickZone.offset().left;
        var oright = clickZone.outerWidth() + oleft;
        var otop = clickZone.offset().top;
        var obottom = clickZone.outerHeight() + otop;

        $("#" + buttonId).mousemove(function (e) {
            var x = e.pageX;
            var y = e.pageY;
            if (!(x < oleft || x > oright || y < otop || y > obottom)) {
                inputFile.offset({ top: y - 15, left: x - 160 });
            } else {
                inputFile.offset({ top: -400, left: -400 });
            }
        });
    }

    document.getElementById(dropZoneId).addEventListener("drop", function (e) {
        $("#" + dropZoneId).removeClass(mouseOverClass);
    }, true);

})
#drop-zone {
    /*Sort of important*/
    width: 300px;
    /*Sort of important*/
    height: 200px;
    position:absolute;
    left:50%;
    top:100px;
    margin-left:-150px;
    border: 2px dashed rgba(0,0,0,.3);
    border-radius: 20px;
    font-family: Arial;
    text-align: center;
    position: relative;
    line-height: 180px;
    font-size: 20px;
    color: rgba(0,0,0,.3);
}

    #drop-zone input {
        /*Important*/
        position: absolute;
        /*Important*/
        cursor: pointer;
        left: 0px;
        top: 0px;
        /*Important This is only comment out for demonstration purposes.
        opacity:0; */
    }

    /*Important*/
    #drop-zone.mouse-over {
        border: 2px dashed rgba(0,0,0,.5);
        color: rgba(0,0,0,.5);
    }


/*If you dont want the button*/
#clickHere {
    position: absolute;
    cursor: pointer;
    left: 50%;
    top: 50%;
    margin-left: -50px;
    margin-top: 20px;
    line-height: 26px;
    color: white;
    font-size: 12px;
    width: 100px;
    height: 26px;
    border-radius: 4px;
    background-color: #3b85c3;

}

    #clickHere:hover {
        background-color: #4499DD;

    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="drop-zone">
    Drop files here...
    <div id="clickHere">
        or click here..
        <input type="file" name="file" id="file" />
    </div>
</div>

The Drag and Drop functionality for this method only works with Chrome, Firefox and Safari. (Don't know if it works with IE10), but for other browsers, the "Or click here" button works fine.

The input field simply follow your mouse when dragging a file over an area, and I've added a button as well..

Uncomment opacity:0; the file input is only visible so you can see what's going on.

Solution 3

This is the "DTHML" HTML5 way to do it. Normal form input (which IS read only as Ricardo Tomasi pointed out). Then if a file is dragged in, it is attached to the form. This WILL require modification to the action page to accept the file uploaded this way.

function readfiles(files) {
  for (var i = 0; i < files.length; i++) {
    document.getElementById('fileDragName').value = files[i].name
    document.getElementById('fileDragSize').value = files[i].size
    document.getElementById('fileDragType').value = files[i].type
    reader = new FileReader();
    reader.onload = function(event) {
      document.getElementById('fileDragData').value = event.target.result;}
    reader.readAsDataURL(files[i]);
  }
}
var holder = document.getElementById('holder');
holder.ondragover = function () { this.className = 'hover'; return false; };
holder.ondragend = function () { this.className = ''; return false; };
holder.ondrop = function (e) {
  this.className = '';
  e.preventDefault();
  readfiles(e.dataTransfer.files);
}
#holder.hover { border: 10px dashed #0c0 !important; }
<form method="post" action="http://example.com/">
  <input type="file"><input id="fileDragName"><input id="fileDragSize"><input id="fileDragType"><input id="fileDragData">
  <div id="holder" style="width:200px; height:200px; border: 10px dashed #ccc"></div>
</form>

It is even more boss if you can make the whole window a drop zone, see How do I detect a HTML5 drag event entering and leaving the window, like Gmail does?

Solution 4

For a native solution without any JS:

<div class="file-area">
    <input type="file">
    <div class="file-dummy">
        <span class="default">Click to select a file, or drag it here</span>
        <span class="success">Great, your file is selected</span>
    </div>
</div>

<style>
    .file-area {
        width: 100%;
        position: relative;
        font-size: 18px;
    }
    .file-area input[type=file] {
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        opacity: 0;
        cursor: pointer;
    }
    .file-area .file-dummy {
        width: 100%;
        padding: 50px 30px;
        border: 2px dashed #ccc;
        background-color: #fff;
        text-align: center;
        transition: background 0.3s ease-in-out;
    }
    .file-area .file-dummy .success {
        display: none;
    }
    .file-area:hover .file-dummy {
        border: 2px dashed #1abc9c;
    }
    .file-area input[type=file]:valid + .file-dummy {
        border-color: #1abc9c;
    }
    .file-area input[type=file]:valid + .file-dummy .success {
        display: inline-block;
    }
    .file-area input[type=file]:valid + .file-dummy .default {
        display: none;
    }
</style>

Adapted from https://codepen.io/Scribblerockerz/pen/qdWzJw

Solution 5

//----------App.js---------------------//
$(document).ready(function() {
    var holder = document.getElementById('holder');
    holder.ondragover = function () { this.className = 'hover'; return false; };
    holder.ondrop = function (e) {
      this.className = 'hidden';
      e.preventDefault();
      var file = e.dataTransfer.files[0];
      var reader = new FileReader();
      reader.onload = function (event) {
          document.getElementById('image_droped').className='visible'
          $('#image_droped').attr('src', event.target.result);
      }
      reader.readAsDataURL(file);
    };
});
.holder_default {
    width:500px; 
    height:150px; 
    border: 3px dashed #ccc;
}

#holder.hover { 
    width:400px; 
    height:150px; 
    border: 3px dashed #0c0 !important; 
}

.hidden {
    visibility: hidden;
}

.visible {
    visibility: visible;
}
<!DOCTYPE html>

<html>
    <head>
        <title> HTML 5 </title>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
    </head>
    <body>
      <form method="post" action="http://example.com/">
        <div id="holder" style="" id="holder" class="holder_default">
          <img src="" id="image_droped" width="200" style="border: 3px dashed #7A97FC;" class=" hidden"/>
        </div>
      </form>
    </body>
</html>
Share:
273,518

Related videos on Youtube

Rudie
Author by

Rudie

I'm a web developer that likes cutting edges and doesn't like the backward kind of compatibility.

Updated on July 08, 2022

Comments

  • Rudie
    Rudie almost 2 years

    These days we can drag & drop files into a special container and upload them with XHR 2. Many at a time. With live progress bars etc. Very cool stuff. Example here.

    But sometimes we don't want that much coolness. What I'd like is to drag & drop files -- many at a time -- into a standard HTML file input: <input type=file multiple>.

    Is that possible? Is there some way to 'fill' the file input with the right filenames (?) from the file drop? (Full filepaths aren't available for file system security reasons.)

    Why? Because I'd like to submit a normal form. For all browsers and all devices. The drag & drop is just progressive enhancement to enhance & simplify UX. The standard form with standard file input (+ multiple attribute) will be there. I'd like to add the HTML5 enhancement.

    edit
    I know in some browsers you can sometimes (almost always) drop files into the file input itself. I know Chrome usually does this, but sometimes it fails and then loads the file in the current page (a big fail if you're filling out a form). I want to fool- & browserproof it.

    • Shark8
      Shark8 over 12 years
      Prepare for some pain if you want to include mac/safari in your compatibilities.
    • Ricardo Tomasi
      Ricardo Tomasi over 12 years
      @Shark8 actually Safari/Mac is one of the few browsers already supporting this.
    • Rudie
      Rudie over 12 years
      Actually, none of the browsers support this. The file input field is read-only (for security) and that's the problem. Stupid security!
    • Ricardo Tomasi
      Ricardo Tomasi over 12 years
      By this i meant "drag & drop files -- many at a time -- into a standard HTML file input".
    • Lloyd
      Lloyd over 12 years
      drag/drop multiple files to input type="file" multiple works fine in Safari
    • Matthew Lock
      Matthew Lock about 11 years
      Firefox (20.0.1 on Windows at least) lets you just drag a file into a input file control.
    • Rudie
      Rudie about 11 years
      The good browsers do, but that's not the issue.
  • Rudie
    Rudie over 12 years
    The file input has a multiple attribute these days. No need for more than 1 file input. That's not the issue though. How do I get the File objects into the file input? I'm thinking this requires some code example...
  • Ricardo Tomasi
    Ricardo Tomasi over 12 years
    @Rudie you can't, that's the problem.
  • Rudie
    Rudie over 12 years
    Can't what? Multiple? Yes, you can. I just said that. The multiple isn't the problem. The getting the files from a (dragged) File object into a file input, that's the problem.
  • Rudie
    Rudie over 12 years
    Like I said in the question: I know XHR2 and I don't want to use it. I guess the iportant part: "the file input is read-only". That sucks... Cancelling the drop event isn't a bad idea! Not as good as I'd hoped, but probably the best. Dropping multiple files works in Chrome too btw. Chrome now also allows uploading directories. All very kewl and not helping my case =(
  • BjarkeCK
    BjarkeCK over 11 years
    That's why i added a button aswell^^ But yah your'e right. I wouln't use it eather... Or would i !?
  • Develoger
    Develoger over 10 years
    Good solution yet it does not work on IE < 10 because IE 9 and less does not support HTML5 files API :(
  • Rudie
    Rudie almost 10 years
    What does it show the user? Can you make a fiddle or online example?
  • nmz787
    nmz787 about 9 years
    I wish I knew how that is supposed to work... it seems all the drag/drop functions have to deal with adding the hover effect... but I really can't tell. Looks good in the fiddle, but I don't think I can use it since I need to support Internet Explorer
  • BjarkeCK
    BjarkeCK about 9 years
    @nmz787 checkout this thread, you might find another solution there. stackoverflow.com/questions/1537223/…
  • Rudie
    Rudie over 8 years
    Actually I like it. And it could work for multiple files too, because JS could make any input 'fly' and keep others down, and dynamically add more.
  • kurdtpage
    kurdtpage over 8 years
    This line: document.getElementById('fileDragData').value = files[i].slice(); is not needed, because it is superseded in the reader.onload function
  • jlb
    jlb almost 8 years
    @Rudie getting dragged file(s) into a file input is possible with Chrome/FF (using the files property), but i haven't managed in IE - have you had any luck?
  • Rudie
    Rudie almost 8 years
    @jlb What do you mean "using the files property"? Could you make an answer with relevant code? What I was looking for doesn't work/exist in any browser.
  • Rudie
    Rudie almost 8 years
    Waaaaaaaat! That works!? That's exactly what I was looking for. Didn't work 2 years ago. Awesome! Of course it doesn't work in IE =) The important question: is there reliable feature detection?, so you can hide the dropzone in IE, bc it won't work.
  • jlb
    jlb almost 8 years
    D'oh, a bit late then :) Right now im just using simple user agent checks in JS. Of course you have to test for MSIE , Trident/ (IE11) and Edge/ (IE12)...
  • Tithen-Firion
    Tithen-Firion almost 8 years
    @BjarkeCK wouldn't it be easier to add click listener to #clickHere and just .click() hidden file input? I mean, a bit shorter and more readable code IMO.
  • John
    John over 7 years
    @PiotrKowalski I think that would potentially trigger a recursive call until the call stack overflows
  • Tithen-Firion
    Tithen-Firion over 7 years
    @John you are right. My browser handles this well (stops after second .click() because file selection window is already open) but you can add click listener to file input and e.stopPropagation() should do the trick.
  • Risadinha
    Risadinha over 7 years
    FF 48.0.2 (Mac) throws "TypeError: setting a property that has only a getter" at line fileInput.files = evt.dataTransfer.files;. Safari and Chrome however both work fine.
  • Russell Smith
    Russell Smith over 7 years
    This example doesn't work on firefox 45 on linux, but it works for me on chrome. I don't get any console errors, it simply doesn't show that any file was dropped.
  • Rudie
    Rudie over 7 years
    Yes, it does these days. Not a trick. Very intentional. Also very intentionally very restricted. You can't add files to the list, or mutate the list at all. Dragging and dropping can remember files, and add onto them, but input.files can't =(
  • Eddie
    Eddie about 7 years
    I ended up using the style only. Making the input 100% width and height worked better than moving it around.
  • JeFf
    JeFf about 7 years
    For adding and removing mouse-over class of the drop-zone it would be better to use dragenter and dragleave events.
  • nick
    nick about 7 years
    Just spent hours trying to get dropzone js to work in an existing form. Copied and pasted this simple answer and works as expected right away. Thanks! Wondering if you have any idea how to make it so you can drag/click multiple files separately and still keep the previous files? I mean if you drag one file and then drag another, it clears the first. Would be perfect if you could keep adding.
  • jlb
    jlb about 7 years
    @nick unfortunately that's not possible with -- sorry :(
  • nick
    nick about 7 years
    actually I made a post to try and find a solution but figured out for myself. Pretty simple change, just fileInputs[index] = ... to pass the file data to a particular input and then call a function showNext to add a new input stackoverflow.com/a/43397640/6392779
  • henryC
    henryC almost 7 years
    Linux / Chrome Version 59.0.3071.115 (Official Build) (64-bit) -- Works! Linux / FF 54.0 -- Does nothing.
  • Dipak
    Dipak almost 7 years
    @Rudie please click run code snippet and drag-n-drop one image to view, it will show the preview of image dropped.
  • William Entriken
    William Entriken over 6 years
    Here is another cute drag and drop application that does NOT involve file uploads. Linking just in case somebody wants to study more. codepen.io/anon/pen/MOPvZK?editors=1010
  • William Entriken
    William Entriken over 6 years
    The IE 10 solution is to degrade and only show the input type=file
  • Kevin Burke
    Kevin Burke over 6 years
    Am I missing something, or do you just constantly overwrite the .value property with the most recent file, every time you iterate through the fore loop?
  • LinusGeffarth
    LinusGeffarth over 6 years
    I had to apply evt.preventDefault(); e.stopPropagation() to all of the following to make this work: drag dragstart dragend dragover dragenter dragleave drop
  • totymedli
    totymedli about 6 years
    This solution doesn't trigger the change event on the input under Firefox, so you have to manually trigger it: fileInput.dispatchEvent(new Event('change')). I have FF 58.
  • TheRedstoneTaco
    TheRedstoneTaco almost 6 years
    I spent two hours wondering why onload never fired. It's because you do: "reader.readAsDataURL(files[i]);" after the onload event instead of inside it. Since I didn't understand how any of this worked and typed out the code for myself I put your curly bracket after that readAs line instead of before it, which caused the issue. It's all because you didn't put your curly closing bracket in a nice spot. Please format your javascript code beautifully instead of succinctly.
  • Fight Fire With Fire
    Fight Fire With Fire almost 6 years
    I get a Uncaught TypeError: Cannot read property 'addEventListener' of null when i use this code - under Chrome - does it not work in latest versions of Chrome?
  • Michael
    Michael almost 6 years
    It works fine for me in the latest version of Chrome. Make sure you use the right ID's
  • jimiayler
    jimiayler over 5 years
    FYI: Fiddle link is broken.
  • Abhishek Singh
    Abhishek Singh over 5 years
    Is there a way to get rid of the "no file chosen" which keeps hovering along with our mouse pointer? @BjarkeCK
  • Nikhil VJ
    Nikhil VJ about 4 years
    how does one fetch the selected file later on when submitting the form?
  • Wajahath
    Wajahath about 3 years
    For me, this is the coolest solution, No JS. Thanks for adding it here. I never knew the <input type="file"> support this drag option natively!
  • Oleksandr Bratashov
    Oleksandr Bratashov about 3 years
    Thanks, works! I've created working link jsfiddle.net/abratashov/n42ohkr3/2
  • Fanky
    Fanky almost 3 years
    @SashaB. no, the fiddle doesn't work in Firefox 91.0.2, Win10. Opens the image instead in the tab.
  • Fanky
    Fanky almost 3 years
    Cool, only it seems input is :valid even when no file selected (FF)
  • Fanky
    Fanky almost 3 years
    Oh it works when the input has required attribute. But I don't want the input required :)
  • Fanky
    Fanky almost 3 years
    Finaly replacing with .file-area input[type=file]:not([value=""]) + ... but adding jQuery( 'input[type=file]' ).change( function( e ) { this.defaultValue = this.value;} ); because DOM value attribute doesn't update otherwise. So not pure-css anymore.
  • Kameron
    Kameron almost 3 years
    @BjarkeCK Thank you for this solution - I was wondering if you know a good way to make it so only the "or click here.." button displays but acts the same as the <input file function.