[html] drag drop files into standard html file input

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.

This question is related to html file-upload drag-and-drop

The answer is


For anyone who's looking to do this in 2018, I've got a much better and simpler solution then all the old stuff posted here. You can make a nice looking drag & drop box with just vanilla HTML, JavaScript and CSS.

(Only works in Chrome so far)

Let's start with the HTML.

<div>
<input type="file" name="file" id="file" class="file">
<span id="value"></span>
</div>

Then we'll get to the styling.

    .file {
        width: 400px;
        height: 50px;
        background: #171717;
        padding: 4px;
        border: 1px dashed #333;
        position: relative;
        cursor: pointer;
    }

    .file::before {
        content: '';
        position: absolute;
        background: #171717;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 100%;
        height: 100%;
    }

    .file::after {
        content: 'Drag & Drop';
        position: absolute;
        color: #808080;
        font-size: 20px;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
    }

After you've done this it already looks fine. But I imagine you'd like to see what file you actaully uploaded, so we're going to do some JavaScript. Remember that pfp-value span? That's where we'll print out the file name.

let file = document.getElementById('file');
file.addEventListener('change', function() {
    if(file && file.value) {
        let val = file.files[0].name;
        document.getElementById('value').innerHTML = "Selected" + val;
    }
});

And that's it.


What you could do, is display a file-input and overlay it with your transparent drop-area, being careful to use a name like file[1]. {Be sure to have the enctype="multipart/form-data" inside your FORM tag.}

Then have the drop-area handle the extra files by dynamically creating more file inputs for files 2..number_of_files, be sure to use the same base name, populating the value-attribute appropriately.

Lastly (front-end) submit the form.


All that's required to handle this method is to alter your procedure to handle an array of files.


In theory, you could add an element overlaying the <input/>, and then use it's drop event to capture the files (using the File API) and pass them to input files array.

Except that a file input is read-only. This is an old problem.

You can however, bypass the form control completely and upload via XHR (not sure about the support for that):

You could also use an element in the surrounding area to cancel the drop event in Chrome, and prevent the default behaviour of loading the file.

Dropping multiple files over the input already works in Safari and Firefox.


For a CSS only solution:

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

.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;
}

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


This is what I came out with.

Using Jquery and Html. This will add it to the insert files.

_x000D_
_x000D_
var dropzone = $('#dropzone')_x000D_
_x000D_
_x000D_
dropzone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {_x000D_
    e.preventDefault();_x000D_
    e.stopPropagation();_x000D_
  })_x000D_
_x000D_
dropzone.on('dragover dragenter', function() {_x000D_
    $(this).addClass('is-dragover');_x000D_
  })_x000D_
dropzone.on('dragleave dragend drop', function() {_x000D_
    $(this).removeClass('is-dragover');_x000D_
  })  _x000D_
  _x000D_
dropzone.on('drop',function(e) {_x000D_
 var files = e.originalEvent.dataTransfer.files;_x000D_
 // Now select your file upload field _x000D_
 // $('input_field_file').prop('files',files)_x000D_
  });
_x000D_
input { margin: 15px 10px !important;}_x000D_
_x000D_
.dropzone {_x000D_
 padding: 50px;_x000D_
 border: 2px dashed #060;_x000D_
}_x000D_
_x000D_
.dropzone.is-dragover {_x000D_
  background-color: #e6ecef;_x000D_
}_x000D_
_x000D_
.dragover {_x000D_
 bg-color: red;_x000D_
}
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>_x000D_
<div class="" draggable='true' style='padding: 20px'>_x000D_
 <div id='dropzone' class='dropzone'>_x000D_
  Drop Your File Here_x000D_
 </div>_x000D_
 </div>
_x000D_
_x000D_
_x000D_


Few years later, I've built this library to do drop files into any HTML element.

You can use it like

const Droppable = require('droppable');

const droppable = new Droppable({
    element: document.querySelector('#my-droppable-element')
})

droppable.onFilesDropped((files) => {
    console.log('Files were dropped:', files);
});

// Clean up when you're done!
droppable.destroy();

This is an improvement, bugfix, and modification of the example that William Entriken gave here. There were some issues with it. For example the normal button from <input type="file" /> didn't do anything (in case the user wanted to upload the file that way).

Notice: I am making a webapp that only I use, so this is only tested (and refined) for Firefox. I am sure though that this code is of value even if you develop for the crossbrowser situation.

_x000D_
_x000D_
function readFile(e) {
  var files;
  if (e.target.files) {
    files=e.target.files
  } else {
    files=e.dataTransfer.files
  }
  if (files.length==0) {
    alert('What you dropped is not a file.');
    return;
  }
  var file=files[0];
  document.getElementById('fileDragName').value = file.name
  document.getElementById('fileDragSize').value = file.size
  document.getElementById('fileDragType').value = file.type
  reader = new FileReader();
  reader.onload = function(e) {
    document.getElementById('fileDragData').value = e.target.result;
  }
  reader.readAsDataURL(file);
}
function getTheFile(e) {
  e.target.style.borderColor='#ccc';
  readFile(e);
}
_x000D_
<input type="file" onchange="readFile(event)">
<input id="fileDragName">
<input id="fileDragSize">
<input id="fileDragType">
<input id="fileDragData">
<div style="width:200px; height:200px; border: 10px dashed #ccc"
     ondragover="this.style.borderColor='#0c0';return false;"       
     ondragleave="this.style.borderColor='#ccc'"       
     ondrop="getTheFile(event); return false;"       
></div>
_x000D_
_x000D_
_x000D_


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.

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

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?


I made a solution for this.

_x000D_
_x000D_
$(function () {_x000D_
    var dropZoneId = "drop-zone";_x000D_
    var buttonId = "clickHere";_x000D_
    var mouseOverClass = "mouse-over";_x000D_
_x000D_
    var dropZone = $("#" + dropZoneId);_x000D_
    var ooleft = dropZone.offset().left;_x000D_
    var ooright = dropZone.outerWidth() + ooleft;_x000D_
    var ootop = dropZone.offset().top;_x000D_
    var oobottom = dropZone.outerHeight() + ootop;_x000D_
    var inputFile = dropZone.find("input");_x000D_
    document.getElementById(dropZoneId).addEventListener("dragover", function (e) {_x000D_
        e.preventDefault();_x000D_
        e.stopPropagation();_x000D_
        dropZone.addClass(mouseOverClass);_x000D_
        var x = e.pageX;_x000D_
        var y = e.pageY;_x000D_
_x000D_
        if (!(x < ooleft || x > ooright || y < ootop || y > oobottom)) {_x000D_
            inputFile.offset({ top: y - 15, left: x - 100 });_x000D_
        } else {_x000D_
            inputFile.offset({ top: -400, left: -400 });_x000D_
        }_x000D_
_x000D_
    }, true);_x000D_
_x000D_
    if (buttonId != "") {_x000D_
        var clickZone = $("#" + buttonId);_x000D_
_x000D_
        var oleft = clickZone.offset().left;_x000D_
        var oright = clickZone.outerWidth() + oleft;_x000D_
        var otop = clickZone.offset().top;_x000D_
        var obottom = clickZone.outerHeight() + otop;_x000D_
_x000D_
        $("#" + buttonId).mousemove(function (e) {_x000D_
            var x = e.pageX;_x000D_
            var y = e.pageY;_x000D_
            if (!(x < oleft || x > oright || y < otop || y > obottom)) {_x000D_
                inputFile.offset({ top: y - 15, left: x - 160 });_x000D_
            } else {_x000D_
                inputFile.offset({ top: -400, left: -400 });_x000D_
            }_x000D_
        });_x000D_
    }_x000D_
_x000D_
    document.getElementById(dropZoneId).addEventListener("drop", function (e) {_x000D_
        $("#" + dropZoneId).removeClass(mouseOverClass);_x000D_
    }, true);_x000D_
_x000D_
})
_x000D_
#drop-zone {_x000D_
    /*Sort of important*/_x000D_
    width: 300px;_x000D_
    /*Sort of important*/_x000D_
    height: 200px;_x000D_
    position:absolute;_x000D_
    left:50%;_x000D_
    top:100px;_x000D_
    margin-left:-150px;_x000D_
    border: 2px dashed rgba(0,0,0,.3);_x000D_
    border-radius: 20px;_x000D_
    font-family: Arial;_x000D_
    text-align: center;_x000D_
    position: relative;_x000D_
    line-height: 180px;_x000D_
    font-size: 20px;_x000D_
    color: rgba(0,0,0,.3);_x000D_
}_x000D_
_x000D_
    #drop-zone input {_x000D_
        /*Important*/_x000D_
        position: absolute;_x000D_
        /*Important*/_x000D_
        cursor: pointer;_x000D_
        left: 0px;_x000D_
        top: 0px;_x000D_
        /*Important This is only comment out for demonstration purposes._x000D_
        opacity:0; */_x000D_
    }_x000D_
_x000D_
    /*Important*/_x000D_
    #drop-zone.mouse-over {_x000D_
        border: 2px dashed rgba(0,0,0,.5);_x000D_
        color: rgba(0,0,0,.5);_x000D_
    }_x000D_
_x000D_
_x000D_
/*If you dont want the button*/_x000D_
#clickHere {_x000D_
    position: absolute;_x000D_
    cursor: pointer;_x000D_
    left: 50%;_x000D_
    top: 50%;_x000D_
    margin-left: -50px;_x000D_
    margin-top: 20px;_x000D_
    line-height: 26px;_x000D_
    color: white;_x000D_
    font-size: 12px;_x000D_
    width: 100px;_x000D_
    height: 26px;_x000D_
    border-radius: 4px;_x000D_
    background-color: #3b85c3;_x000D_
_x000D_
}_x000D_
_x000D_
    #clickHere:hover {_x000D_
        background-color: #4499DD;_x000D_
_x000D_
    }
_x000D_
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>_x000D_
<div id="drop-zone">_x000D_
    Drop files here..._x000D_
    <div id="clickHere">_x000D_
        or click here.._x000D_
        <input type="file" name="file" id="file" />_x000D_
    </div>_x000D_
</div>
_x000D_
_x000D_
_x000D_

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.


_x000D_
_x000D_
//----------App.js---------------------//_x000D_
$(document).ready(function() {_x000D_
    var holder = document.getElementById('holder');_x000D_
    holder.ondragover = function () { this.className = 'hover'; return false; };_x000D_
    holder.ondrop = function (e) {_x000D_
      this.className = 'hidden';_x000D_
      e.preventDefault();_x000D_
      var file = e.dataTransfer.files[0];_x000D_
      var reader = new FileReader();_x000D_
      reader.onload = function (event) {_x000D_
          document.getElementById('image_droped').className='visible'_x000D_
          $('#image_droped').attr('src', event.target.result);_x000D_
      }_x000D_
      reader.readAsDataURL(file);_x000D_
    };_x000D_
});
_x000D_
.holder_default {_x000D_
    width:500px; _x000D_
    height:150px; _x000D_
    border: 3px dashed #ccc;_x000D_
}_x000D_
_x000D_
#holder.hover { _x000D_
    width:400px; _x000D_
    height:150px; _x000D_
    border: 3px dashed #0c0 !important; _x000D_
}_x000D_
_x000D_
.hidden {_x000D_
    visibility: hidden;_x000D_
}_x000D_
_x000D_
.visible {_x000D_
    visibility: visible;_x000D_
}
_x000D_
<!DOCTYPE html>_x000D_
_x000D_
<html>_x000D_
    <head>_x000D_
        <title> HTML 5 </title>_x000D_
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>_x000D_
    </head>_x000D_
    <body>_x000D_
      <form method="post" action="http://example.com/">_x000D_
        <div id="holder" style="" id="holder" class="holder_default">_x000D_
          <img src="" id="image_droped" width="200" style="border: 3px dashed #7A97FC;" class=" hidden"/>_x000D_
        </div>_x000D_
      </form>_x000D_
    </body>_x000D_
</html>
_x000D_
_x000D_
_x000D_


Awesome work by @BjarkeCK. I made some modifications to his work, to use it as method in jquery:

$.fn.dropZone = function() {
  var buttonId = "clickHere";
  var mouseOverClass = "mouse-over";

  var dropZone = this[0];
  var $dropZone = $(dropZone);
  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[type='file']");
  dropZone.addEventListener("dragleave", function() {
    this.classList.remove(mouseOverClass);
  });
  dropZone.addEventListener("dragover", function(e) {
    console.dir(e);
    e.preventDefault();
    e.stopPropagation();
    this.classList.add(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);
  dropZone.addEventListener("drop", function(e) {
    this.classList.remove(mouseOverClass);
  }, true);
}

$('#drop-zone').dropZone();

Working Fiddle


I know some trick works in Chrome:

When dropping files into drop zone you get a dataTransfer.files object, that is a FileList type of object, that contains all the files you dragged. Meanwhile, <input type="file" /> element has the property files, that is the same FileList type object.

So, you can simply assign the dataTransfer.files object to the input.files property.


Easy and simple. You don't need create a new FormData or do an Ajax to send image. You can put dragged files in your input field.

$dropzone.ondrop = function (e) {
    e.preventDefault();
    input.files = e.dataTransfer.files;
}

_x000D_
_x000D_
var $dropzone = document.querySelector('.dropzone');
var input = document.getElementById('file-upload');

$dropzone.ondragover = function (e) { 
  e.preventDefault(); 
  this.classList.add('dragover');
};
$dropzone.ondragleave = function (e) { 
    e.preventDefault();
    this.classList.remove('dragover');
};
$dropzone.ondrop = function (e) {
    e.preventDefault();
    this.classList.remove('dragover');
    input.files = e.dataTransfer.files;
}
_x000D_
.dropzone {
  padding: 10px;
  border: 1px dashed black;
}
.dropzone.dragover {
  background-color: rgba(0, 0, 0, .3);
}
_x000D_
<div class="dropzone">Drop here</div>
<input type="file" id="file-upload" style="display:none;">
_x000D_
_x000D_
_x000D_


Examples related to html

Embed ruby within URL : Middleman Blog Please help me convert this script to a simple image slider Generating a list of pages (not posts) without the index file Why there is this "clear" class before footer? Is it possible to change the content HTML5 alert messages? Getting all files in directory with ajax DevTools failed to load SourceMap: Could not load content for chrome-extension How to set width of mat-table column in angular? How to open a link in new tab using angular? ERROR Error: Uncaught (in promise), Cannot match any routes. URL Segment

Examples related to file-upload

bootstrap 4 file input doesn't show the file name How to post a file from a form with Axios File Upload In Angular? How to set the max size of upload file The request was rejected because no multipart boundary was found in springboot Send multipart/form-data files with angular using $http File upload from <input type="file"> How to upload files in asp.net core? REST API - file (ie images) processing - best practices Angular - POST uploaded file

Examples related to drag-and-drop

Drag and drop menuitems How to automate drag & drop functionality using Selenium WebDriver Java drag drop files into standard html file input How do I remove a file from the FileList Is there a good jQuery Drag-and-drop file upload plugin? Drag and drop elements from list into separate blocks How do I get the coordinate position after using jQuery drag and drop? How do I drag and drop files into an application?