Programmatically Setting File Inputs in JavaScript
Published: 2023-12-16 11:30:32

Setting the value of a text or number input is as simple as doing

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

```js
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:

```js
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]`.

```js
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](https://w3c.github.io/FileAPI/#filelist-section)

#### My Approach

While searching for answers, I got a bunch of [frowning faces](https://stackoverflow.com/a/12999864/14508232) and [NOs](https://stackoverflow.com/a/6021545/14508232) from Stackoverflow. One [answer](https://stackoverflow.com/a/8120125/14508232) 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:

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

```js
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.