How to set file input value when dropping file on page? [duplicate]
Asked Answered
O

1

84

I'm attempting to make a control where you can both select a file to submit in the form and drop a file into it to do the same thing. I have something like this where it will also show a preview of the file if it's an image:

<div class="preview-image-container">

  <input type="file" name="File" id="File" accept="image/*" 
         class="image-url form-control" style="display:none;" />

  <div class="preview-image-dummy"></div><img class="preview-image-url" />
  <div class="preview-image-instruction">
    <div class="preview-image-btn-browse btn btn-primary">Select file</div>
    <p>or drop it here.</p>
  </div>

</div>

Here's a fiddle with MCVE.

The user drops the file in the preview-image-container. The file isn't submitted with AJAX, the user needs to submit the form which contain more data.

For security reasons, we aren't allowed to change the value of the input file with JavaScript. However, I know that the default input file support droppable and there's a bunch of websites that let us select files by dropping them in the form so my guess is that there's a way to do it.

As you can see from the MCVE, I am only interested in using jQuery or pure JavaScript without additional libraries.

Although dropzone.js could be used, it doesn't fit in my requirements and it would require a considerate amount of time to customize its look. Moreover, dropzone.js have certain configuration requirements that can't be met within my ASP.NET application.


There's a similar question but does not have a proper answer:
How to set file object into input file in the form in HTML?

Also not similar to drag drop images input file and preview before upload because I have already achieved drag-drop and preview action. My question is specific to the problem of having an empty file input when dropping the file in a parent container.

Overwhelm answered 27/11, 2017 at 16:10 Comment(3)
A quick search shows this has been asked lots of times before, eg: #25093481Putrescine
@freedomn-m that question is specific to showing a preview on drop, which I'm already doing. However, my input is empty when dropping.Overwhelm
@freedomn-m the moment you open a similar so question you can actually get in kind of a question loop.. :/Profluent
U
140

Disclaimer: Correct as of December 2017 and for modern browsers only.


TL;DR: Yes, now you can!

*if you have a dataTransfer or FileList object

Previously, programmatically changing the files input[type=file] field was disabled due to legacy security vulnerabilities, which are fixed on modern browsers.

The last of the major browsers (Firefox), has recently enabled us to set the files for the input file field. According to testing done by W3C, it seems that you can already do this on Google Chrome!

Relevant screenshot and text quoted from MDN:

enter image description here

You can set as well as get the value of HTMLInputElement.files in all modern browsers.
Source and browser compatibility, see MDN

The Firefox bugfix discussion thread has this demo you can test it out on, and here's the source if you want to edit it. For future reference in case this link dies, I will also include it as a runnable snippet below:

let target = document.documentElement;
let body = document.body;
let fileInput = document.querySelector('input');

target.addEventListener('dragover', (e) => {
  e.preventDefault();
  body.classList.add('dragging');
});

target.addEventListener('dragleave', () => {
  body.classList.remove('dragging');
});

target.addEventListener('drop', (e) => {
  e.preventDefault();
  body.classList.remove('dragging');
  
  fileInput.files = e.dataTransfer.files;
});
body {
  font-family: Roboto, sans-serif;
}

body.dragging::before {
  content: "Drop the file(s) anywhere on this page";
  position: fixed;
  left: 0; width: 100%;
  top: 0; height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1.5em;
  background-color: rgba(255, 255, 0, .3);
  pointer-events: none;
}

button, input {
  font-family: inherit;
}

a {
  color: blue;
}
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
      <link href="https://fonts.googleapis.com/css?family=Roboto:400,400i,700" rel="stylesheet"> 

  <title>JS Bin</title>
</head>
<body>
  
  <h1>Drag and drop files into file input</h1>
  
  <p><small>Supported in <a href="https://github.com/whatwg/html/issues/2861">WebKit and Blink</a>. To test, drag and drop one or more files from your operating system onto this page.</small></p>
  
  <p>
    <input type="file">
  </p>

</body>
</html>

Important note:

You can only do this if you have an existing FileList OR dataTransfer object that you are able to set to the input file element (as the setter method DOES NOT accept plain text strings).

For further information, see Kaiido's answer here: How to set File objects and length property at FileList object where the files are also reflected at FormData object?


Now to answer your original question, it should be as simple as this:

document.querySelector('.preview-image-instruction')
    .addEventListener('drop', (ev) => {
        ev.preventDefault();
        document.querySelector('.image-url').files = ev.dataTransfer.files;
    });
Universalize answered 28/11, 2017 at 2:8 Comment(11)
You are a life saver, thank you very much. It won't work on IE 11.0.9600 and I have yet to test this on Edge but if I find a workaround for that browser I will gladly post an additional answer.Overwhelm
Yeah, this is a game changer, it surprised me as well. Seems like Edge passed the W3C testing also - see updated answer.Universalize
Important note: You can only set the input.files property to an other FileList object, and since we cannot create nor modify (except for clearing) FileList objects, this means that we still cannot append arbitrary files in this property, only the ones that come from an other input or from a DataTransferEvent. See #47119926Terle
Important note #2 As discovered by SO user guest271314, there is actually now a way to create an writable FileList through the DataTransfer.DataTransfer constructor (currently only available in chrome). I did update the linked answer with more info.Terle
This is really cool stuff.. One thing I noticed here is, when we assign files like this to input file element, its not generating onchange event of input file type. Is there any workaroud for this ?Succession
I have a file input in a form, I remember the FileList object of my file input in a variable, then I call .reset() on the form. After that I want to set my remembered FileList variable to the file input again. But the FileList in my variable seems to be cleared by the forms .reset() method. Does someone have an idea how to solve this??Krystin
Thanks so much for this answer. I actually gave up because answers to other questions said this was not possible without AJAX. With this I was able to dive back in and get my feature working perfectly!Noncombatant
This sample doesn't seem to work in Edge (Version 44.18362.387.0). Strangely the MDN Webdocs claim that this feature is supported by Edge. See developer.mozilla.org/en-US/docs/Web/API/… Does anyone know a workaround for Edge?Immediately
Actually the "new" MS Edge based on Chromium DOES support this feature. You have to love Microsoft for their naming non-sense.Immediately
The easiest way to do it is using javascript like this: <input type="file" /> <script> // Get a reference to our file input const fileInput = document.querySelector('input[type="file"]'); // Create a new File object const myFile = new File(['Hello World!'], 'myFile.txt', { type: 'text/plain', lastModified: new Date(), }); // Now let's create a DataTransfer to get a FileList const dataTransfer = new DataTransfer(); dataTransfer.items.add(myFile); fileInput.files = dataTransfer.files; </script>Derringdo
I get "Request entity too large" when dropping more than 1 fileSakti

© 2022 - 2024 — McMap. All rights reserved.