Actually, there is a way to do that. It's far from straightforward, but it does work.
The only way to create a FileList object is to create a custom DataTransfer object. You can add files to it using dataTransfer.items.add(), and obtain the corresponding FileList through dataTransfer.files.
So, create a new DataTransfer object every time you want to add files, add the existing and the new files to it, and assign its FileList to the files property of the input element.
Note: You can't use the drop event's DataTransfer object for this, because it's read-only.
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Create a new DataTransfer object
const dataTransfer = new DataTransfer
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Add existing files from the input element
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
You can also reuse the same DataTransfer object, so you don't have to re-add the existing files.
However, in this case, you also have to handle input events on the input element.
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
const dataTransfer = new DataTransfer
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
InputElement.addEventListener('input', e => {
dataTransfer.items.clear()
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
})
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
Or, if you want to add the files when interacting with the file input instead of replacing them, you can do this:
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
const dataTransfer = new DataTransfer
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
InputElement.addEventListener('input', e => {
e.preventDefault()
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
InputElement.files = dataTransfer.files
})
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
</div>
<input type="submit" value="Отправить">
</form>
You can remove files from the DataTransfer object using dataTransfer.items.remove():
document.querySelectorAll('.drop_zone__input').forEach(InputElement => {
const dropZoneElement = InputElement.closest('.drop_zone');
const removeFirstElement = dropZoneElement.querySelector('.drop_zone__remove_first')
const dataTransfer = new DataTransfer
dropZoneElement.addEventListener('dragover', e => {
e.preventDefault()
});
dropZoneElement.addEventListener('drop', e => {
e.preventDefault();
//Add new files from the event's DataTransfer
for(let i = 0; i < e.dataTransfer.files.length; i++)
dataTransfer.items.add(e.dataTransfer.files[i])
//Assign the files to the input element
InputElement.files = dataTransfer.files
});
InputElement.addEventListener('input', e => {
e.preventDefault()
for(let i = 0; i < InputElement.files.length; i++)
dataTransfer.items.add(InputElement.files[i])
InputElement.files = dataTransfer.files
})
removeFirstElement.addEventListener('click', () => {
dataTransfer.items.remove(0)
InputElement.files = dataTransfer.files
})
})
.drop_zone{
height: 200px;
width: 200px;
border: solid black 1px;
}
<form action="" method="POST" enctype="multipart/form-data">
<div class="drop_zone">
<span class="drop_zone__prompt">Drop your files here</span>
<input required name="images" type="file" multiple class="drop_zone__input">
<input type="button" class="drop_zone__remove_first" value="Remove first file">
</div>
<input type="submit" value="Отправить">
</form>