Setting the value of a text or number input is as simple as doing
const input = document.querySelector('input');
input.value = 'new value';
However, the situation is slightly different for file inputs. Unlike text or number fields, simply setting the value
of a file input is ineffective.
Here is what the value of a file input looks like when the user selects a file manually
input.addEventListener('change', (event) => {
console.log(event.target.value);
// => C:\\fakepath\\file.txt
});
Common Misconceptions and Attempts
The path to the file C:\\fakepath\\file.txt
on the user's system is obscured from the browser, and setting the value property to something else wouldn't make any difference as the browser does not rely on the value of the input to get a reference to the file. Under the hood, the browser keeps an internal reference to the file on the user's disk, but this is not available to the DOM and should not be changed. You can modify the file, however, by programmatically setting the file property on the input element.
And, no, it is not as simple as doing:
const file = 'path/to/my_file.ext';
input.files = file;
// OR
input.files[0] = file;
Or creating a file object and assigning it to files[0]
.
const myFile = new File(['My File Content'], 'my_file.txt');
input.files[0] = myFile; // WON'T WORK
input.files = [myFile]; // WON'T WORK
That wouldn't work either as the files
object, which is a type of the FileList
interface, is not internally an array, but an array-like object. See w3c specification here
My Approach
While searching for answers, I got a bunch of frowning faces and NOs from Stackoverflow. One answer says to a PHP user that if there's a workaround, it will eventually be disabled by Chrome builders. However, this is not the case with the solution here as to disable this would be to disable the drag and drop feature simulation (which is used in most testing libraries), custom drag/drop interaction, or custom clipboard operations. The drag-and-drop feature is what the solution is based on.
Here is it:
const fileInput = document.querySelector('input[type="file"]');
// Get your file ready
const myFileContent = ['My File Content'];
const myFileName = 'my_file.txt';
const myFile = new File(myFileContent, myFileName);
// Create a data transfer object. Similar to what you get from a `drop` event as `event.dataTransfer`
const dataTransfer = new DataTransfer();
// Add your file to the file list of the object
dataTransfer.items.add(file);
// Save the file list to a new variable
const fileList = dataTransfer.files;
// Set your input `files` to the file list
fileInput.files = fileList;
Depending on your use case, you can trigger a change and/or input event to simulate an actual user interaction
fileInput.dispatchEvent(new Event('change', { bubbles: true }));
// AND/OR
fileInput.dispatchEvent(new Event('input', { bubbles: true }));
In my case, I had a problem where I needed to change the file content of a file input field in a form of which I had no access to the code. The forms underlying code watched for file input changes or the dragover/drop JavaScript event. This solution helped me in perfectly simulating a user interaction, I hope it works for your use case, too.
Top comments (4)
That is was exactly what I needed. Thanks!
small typo:
dataTransfer.items.add(file);
should bedataTransfer.items.add(myFile);
Awesome!!!
Thank you very much!