Firefox下粘贴剪贴板内容后input files数组读取异常问题
问题描述
我制作了一个最多支持上传5个文件的表单,初始仅显示一个<input type="file">,用户选择文件后会动态添加新的input,使用文件选择器时功能正常。添加剪贴板粘贴(CTRL+V)图片功能后,Firefox中出现异常:第二次粘贴时,$('.form-post-file-input')返回的第0个元素的.files数组为空,第1个元素包含剪贴板内容;虽然提交表单时第0个文件能正常上传,但读取.files却为空。该问题仅在Firefox中出现,Chromium内核的Brave浏览器无此问题。
复现代码
<!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>
解决方案
问题根源
Firefox对直接赋值input.files设置文件的处理逻辑和Chromium存在差异:
- 直接赋值不会触发
input的change事件,导致原有状态判断逻辑失效 - 后续读取旧input的
files属性时,Firefox的安全机制会返回空数组,但实际表单提交时文件数据是保留的
修改方案
调整addNovoInputFile的判断逻辑,改用自定义标记统计已填充文件的input数量,同时优化粘贴事件的文件设置方式:
var addNovoInputFile = function(elem, max){ const fileInputs = $('.form-post-file-input'); // 统计已包含文件的input数量,兼容Firefox下粘贴文件的情况 let filledCount = 0; fileInputs.each(function() { if(this.files.length > 0 || this.dataset.hasFile === 'true'){ filledCount++; } }); // 已达上限或当前input无文件时,不添加新input if(filledCount >= max || (elem.files.length === 0 && elem.dataset.hasFile !== 'true')){ 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"); }; window.addEventListener('paste', e => { let inputs = document.getElementsByName('arquivos[]'); let inputToChange = inputs[inputs.length-1]; if(e.clipboardData.files.length > 0){ // 使用DataTransfer设置文件,同时标记该input已填充 const dt = new DataTransfer(); dt.items.add(e.clipboardData.files[0]); inputToChange.files = dt.files; inputToChange.dataset.hasFile = 'true'; addNovoInputFile(inputToChange, 5); } });
关键修改点
- 新增
dataset.hasFile自定义属性,标记通过粘贴填充的input,解决Firefox下files读取为空的判断问题 - 替换原有循环检查逻辑,通过统计
filledCount判断已填充文件的input数量 - 使用
DataTransfer对象设置粘贴文件,让Firefox能正确识别文件状态
内容的提问来源于stack exchange,提问作者Fnr
相关产品推荐
相关产品推荐

