skip to Main Content

I have a form in which I let the user add up to 5 files in a way that I only show one <input type="file"> and if the user do put a file at the <input> then I update the form adding another input and so on. It works pretty well when I use the file picker and choose a file. But now I’m trying to add an option so the user can just CTRL+V an image clipboard content directly into the <input> field and then update the form as said before. I also added a loop verifying if the input fields do have content so the user don’t update, like, the 0th input and then a new input emerges for no necessity. And that’s the point that making my code fail as shown below:

EDIT I’ve just added an entire HTML file that simulates the issue:

EDIT 2 This issue didn’t happen under Chromium based Brave browser, I can simulate it under Firefox

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <title>Document</title>
</head>
<body>
    <form class="form-post" role="form" method="POST" enctype="multipart/form-data" action="/someroute">
        <!-- other inputs... -->
        <div id="form-post-file-input-div">
            <div class="form-post-file-input-box">
                <input class="novo-post-form-item form-post-file-input" name="arquivos[]" type="file" onchange="addNovoInputFile(this, 5)">
            </div>    
        </div>
        </form>
        
        <script>
var addNovoInputFile = function(elem, max){
    fileInputs = $('.form-post-file-input');

    // When this loop is reached from a copypaste event, the content of fileInputs[0].files is empty
    // this doesn't happen when I just select a file from filepicker 
    for(var i=0; i < fileInputs.length; i++)
    {
        if(fileInputs[i].files.length === 0)
            return;
    }
    // I put this loop so I don't update the form in case, for example, if the user keeps updating a file at the 0th <input>
    // but maybe my solution is just changing the logic of this loop
    
    if(fileInputs.length >= max || elem.files.length === 0)
        return;
    
    var novoInput = "<div class="form-post-file-input-box">";
    novoInput += "<input class="novo-post-form-item form-post-file-input" name="arquivos[]" type="file" onchange="addNovoInputFile(this, " + max + ")">";
    novoInput += "</div>"
    
    $(novoInput).appendTo(".form-post #form-post-file-input-div");
};

// New event I just added in order to fill the input with a printscreen content in the clipboard
window.addEventListener('paste', e => {
    let inputs = document.getElementsByName('arquivos[]');
    let inputToChange = inputs[inputs.length-1];

    if(e.clipboardData.files.length > 0){
        inputToChange.files = e.clipboardData.files;
        addNovoInputFile(inputToChange, 5);
    }
});


        </script>
</body>
</html>

What exactly is happening is that when I run CTRL+V the second time the 0th element from the list returned by $('.form-post-file-input') has the .files array empty and the 1st has the content from the clipboard. Why does that happen since when I typed CTRL+V for the first time I did fill the content of .files (in addEventListener) and the proof for that is that when I submit the form the 0th file is uploaded to the server… but the .files is empty… I don’t understand why specially because this doesn’t happen when I choose files from the file picker.

2

Answers


  1. The cause is use of return; statement which will exit the whole function instead of for loop thus it won’t create another input picker.
    You must use break; instead:

        // When this loop is reached from a copypaste event, the content of fileInputs[0].files is empty
        // this doesn't happen when I just select a file from filepicker 
        for(var i=0; i < fileInputs.length; i++)
        {
            if(fileInputs[i].files.length === 0)
                return;
        }
    

    to

        // When this loop is reached from a copypaste event, the content of fileInputs[0].files is empty
        // this doesn't happen when I just select a file from filepicker 
        for(var i=0; i < fileInputs.length; i++)
        {
            if(fileInputs[i].files.length === 0)
                break; // Change
        }
    

    it worked in chromium based browsers because fileInputs[i].files.length was not 0 over there.

    Login or Signup to reply.
  2. I have checked above code and unable to reproduce issue which you have mentioned but I have found one another issue.

    The issue is when I tried to copy multiple files and pasted it in one go. all the files was pasted in one file input instead of separate input for each pasted files. So I have just fixed those by following issue:

    <script>
        const MAX_FILES = 5;
    
        var addNovoInputFile = function(elem, max) {
            var fileInputs = $('.form-post-file-input');
            var totalFiles = 0;
    
            for (var i = 0; i < fileInputs.length; i++) {
                totalFiles += fileInputs[i].files.length;
            }
    
            // If total files exceed max limit, stop adding more inputs
            if (totalFiles >= max) return;
    
            if (elem.files.length > 0) {
                var novoInput = "<div class="form-post-file-input-box">";
                novoInput += "<input class="novo-post-form-item form-post-file-input" name="arquivos[]" type="file" onchange="addNovoInputFile(this, " + max + ")">";
                novoInput += "</div>";
                $(novoInput).appendTo("#form-post-file-input-div");
            }
        };
    
        window.addEventListener('paste', e => {
            let inputs = document.getElementsByName('arquivos[]');
            let totalFiles = 0;
    
            for (let i = 0; i < inputs.length; i++) {
                totalFiles += inputs[i].files.length;
            }
    
            // If total files exceed max limit, stop processing paste
            if (totalFiles >= MAX_FILES) return;
    
            let pastedFiles = e.clipboardData.files;
            let filesToAdd = Math.min(pastedFiles.length, MAX_FILES - totalFiles);
            for (let i = 0; i < filesToAdd; i++) {
                var novoInput = "<div class="form-post-file-input-box">";
                novoInput += "<input class="novo-post-form-item form-post-file-input" name="arquivos[]" type="file" onchange="addNovoInputFile(this, " + MAX_FILES + ")">";
                novoInput += "</div>";
                $(novoInput).appendTo("#form-post-file-input-div");
                let newInput = $("#form-post-file-input-div").children().last().find("input")[0];
    
                let dt = new DataTransfer();
                dt.items.add(pastedFiles[i]);
                newInput.files = dt.files;
            }
        });
    </script>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search