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>