drag drop files into standard html file input

JavascriptHtmlFile UploadDrag and-Drop

Javascript Problem Overview


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.

Javascript Solutions


Solution 1 - Javascript

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

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

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 https://stackoverflow.com/questions/3144881/how-do-i-detect-a-html5-drag-event-entering-and-leaving-the-window-like-gmail-d

Solution 4 - Javascript

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

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

Solution 6 - Javascript

This is what I came out with.

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

var dropzone = $('#dropzone')


dropzone.on('drag dragstart dragend dragover dragenter dragleave drop', function(e) {
    e.preventDefault();
    e.stopPropagation();
  })

dropzone.on('dragover dragenter', function() {
    $(this).addClass('is-dragover');
  })
dropzone.on('dragleave dragend drop', function() {
    $(this).removeClass('is-dragover');
  })  
  
dropzone.on('drop',function(e) {
	var files = e.originalEvent.dataTransfer.files;
	// Now select your file upload field 
	// $('input_field_file').prop('files',files)
  });

input {	margin: 15px 10px !important;}

.dropzone {
	padding: 50px;
	border: 2px dashed #060;
}

.dropzone.is-dragover {
  background-color: #e6ecef;
}

.dragover {
	bg-color: red;
}

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div class="" draggable='true' style='padding: 20px'>
	<div id='dropzone' class='dropzone'>
		Drop Your File Here
	</div>
	</div>

Solution 7 - Javascript

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.

Solution 8 - Javascript

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.

osx users: maybe in osx you need unmaximize your browser to drag files. why? idk.

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

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

.dropzone {
  padding: 10px;
  border: 1px dashed black;
}
.dropzone.dragover {
  background-color: rgba(0, 0, 0, .3);
}

<div class="dropzone">Drop here</div>
<input type="file" id="file-upload" style="display:none;">

Solution 9 - Javascript

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.

Solution 10 - Javascript

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.

Solution 11 - Javascript

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

Solution 12 - Javascript

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();
                

Solution 13 - Javascript

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.

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

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

Solution 14 - Javascript

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRudieView Question on Stackoverflow
Solution 1 - JavascriptjlbView Answer on Stackoverflow
Solution 2 - JavascriptBjarkeCKView Answer on Stackoverflow
Solution 3 - JavascriptWilliam EntrikenView Answer on Stackoverflow
Solution 4 - JavascriptJonathanView Answer on Stackoverflow
Solution 5 - JavascriptDipakView Answer on Stackoverflow
Solution 6 - JavascriptLionel YeoView Answer on Stackoverflow
Solution 7 - JavascriptRicardo TomasiView Answer on Stackoverflow
Solution 8 - JavascriptGuilherme IazzettaView Answer on Stackoverflow
Solution 9 - JavascriptTimur GilauriView Answer on Stackoverflow
Solution 10 - JavascriptMichaelView Answer on Stackoverflow
Solution 11 - JavascriptMr_GreenView Answer on Stackoverflow
Solution 12 - JavascriptJoel HernandezView Answer on Stackoverflow
Solution 13 - JavascriptMagnusView Answer on Stackoverflow
Solution 14 - JavascriptShark8View Answer on Stackoverflow