How to get multiple files from user separately?

Currently, I’m developing a drag and drop feature. The problem is that I can’t figure out how to get multiple files from user separately. Let’s say that we have a drop zone container and when user drops there images, it assigns them to <input type="file">. Let’s say, a user drops here an image and then decides to drop another image and we have to somehow add this second image to the input. I tried finding solution in the Internet(of course :)) but found nothing that solves this problem.

Here is my HTML:

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

JavaScript:

document.querySelectorAll('.drop_zone__input').forEach(InputElement => {

    const dropZoneElement = InputElement.closest('.drop_zone');

    dropZoneElement.addEventListener('drop', e => {
        e.preventDefault();

        if (e.dataTransfer.files.length){
            InputElement.files = e.dataTransfer.files;
        }
    });

I tried this:

dropZoneElement.addEventListener('drop', e => {
    e.preventDefault();

    if (e.dataTransfer.files.length){
        myfiles = e.dataTransfer.add(InputElement.files);
        InputElement.files = myfiles;
    }
});

But it returns error saying that ‘e.dataTransfer.add is not a function’

Why I tried this:

I found add() method here

And this article says:

The DataTransferItemList object is a list of DataTransferItem objects representing items being dragged. During a drag operation, each DragEvent has a dataTransfer property and that property is a DataTransferItemList.

Answer

You can’t add to the files to the input‘s list of files, although apparently you can replace that list as described by FZs, which largely comes to the same thing.

Another way to deal with this is to make the original input hidden via CSS and add a new input where it used to be, so it can receive new files. You can make the UI clean by listing all of the files you’re going to upload separately from the input.

How you deal with on submission depends on how you’re handling submission.

If you’re doing a standard HTTP form submission, ensure that your server side script handles all of the files regardless of which input they came from (it will receive more than one).

If you’re submitting via ajax, you can use a FormData object and append each file to it, then submit that.

Here’s a quick and dirty example maintaining the files in a FormData object:

let nextFileNum = 1;
const formData = new FormData();
const fileList = document.getElementById("file-list");
document.getElementById("inputs").addEventListener("input", function() {
    let input = event.target.closest("input[type=file]");
    if (!input) {
        return;
    }
    for (const file of input.files) {
        const fileId = `file${nextFileNum++}`;
        formData.append(fileId, file, file.name);
        debugShowCurrentFiles("added");
        let div = document.createElement("div");
        // This is a Q&D sketch, needs a11y
        div.innerHTML = "<span tabindex class=delete>[X]</span><span class=filename></span>";
        div.querySelector(".filename").textContent = file.name;
        div.querySelector(".delete").addEventListener("click", function() {
            formData.delete(fileId);
            debugShowCurrentFiles("deleted");
            div.remove();
            div = null;
        });
        fileList.appendChild(div);
    }
    this.removeChild(input);
    this.insertAdjacentHTML("beforeend", input.outerHTML);
    input = null;
});
function debugShowCurrentFiles(action) {
    const contents = [...formData.entries()];
    console.log(`--- ${action}, updated formData contents (${contents.length}):`);
    for (const [key, value] of contents) {
        console.log(`${key}: ${value.name}`);
    }
}
#file-list .delete {
    margin-right: 2em;
    cursor: pointer;
}
<div id="inputs">
    <input type="file" multiple>
</div>
<div id="file-list"></div>

Leave a Reply

Your email address will not be published. Required fields are marked *