JS多文件上传删除异常:循环重复执行与文件批量丢失问题排查修复
多文件上传删除功能问题排查与修复
问题现象
- 单个文件输入框上传多张图片后,删除第一个位置的图片会导致该输入框内所有文件丢失
- 删除操作仅影响当前输入框,不会波及其他输入框
- 点击第一个
.remove元素时,console.log(key)会执行多次(次数等于上传图片数量)
问题根源
- 重复绑定点击事件:每次上传图片时,都会对所有
.remove元素重新绑定点击事件。比如上传5张图,每个删除按钮会被绑定5次点击事件,导致点击一次触发多次执行。 - 索引变量混淆:删除事件中使用的
key是.each()循环的索引,这个索引和文件在dataTransfer中的实际索引不匹配,重复绑定后每次绑定的key值是循环时的索引,导致删除时错误移除多个文件。 - 事件绑定时机错误:在
fileReader.onloadend中绑定事件,每次加载完一张图片就重新绑定所有删除按钮的事件,造成重复绑定。
修复代码
HTML(无需修改)
<script src="https://code.jquery.com/jquery-3.6.4.js" integrity="sha256-a9jBBRygX1Bh5lt8GZjXDzyOB+bWve9EiO7tROUtj/E=" crossorigin="anonymous"></script> <form action="" method="post" enctype="multipart/form-data"> <div class="field" align="left"> <div class="upload__files-wrap"></div> <input type="file" class="files" accept="image/*" name="hinhanh1[]" multiple /> </div> <div class="field" align="left"> <div class="upload__files-wrap"></div> <input type="file" class="files" accept="image/*" name="hinhanh2[]" multiple /> </div> <br> <input type="submit" value="submit" name="submit"> </form>
CSS(无需修改)
input[type="file"] { display: block; } .upload__files-wrap { display: flex; flex-wrap: wrap; margin: 0 -10px; } .imageThumb { height: 200px; width: 250px; } .pip { display: inline-block; margin: 10px 10px 0 0; position: relative; } .remove { width: 24px; height: 24px; border-radius: 50%; background-color: rgba(0, 0, 0, 0.5); position: absolute; top: 10px; right: 10px; text-align: center; line-height: 24px; z-index: 1; cursor: pointer; } .remove:after { content: "\2716"; font-size: 14px; color: white; }
JavaScript(修复后)
$(function () { if (window.File && window.FileList && window.FileReader) { $(".files").each(function (inputKey) { var files_name_arr = []; var dataTransfer = new DataTransfer(); const $wrap = $(this).prev(".upload__files-wrap"); // 事件委托绑定删除事件,避免重复绑定 $wrap.on("click", ".remove", function() { const $pip = $(this).closest(".pip"); const fileName = $pip.find(".imageThumb").attr("title"); const fileIndex = files_name_arr.indexOf(fileName); if (fileIndex !== -1) { dataTransfer.items.remove(fileIndex); files_name_arr.splice(fileIndex, 1); $pip.remove(); $(".files")[inputKey].files = dataTransfer.files; } }); $(this).change(function (e) { // 添加新文件到dataTransfer,避免重复 for (let file of e.target.files) { if (!files_name_arr.includes(file.name)) { dataTransfer.items.add(file); files_name_arr.push(file.name); // 读取并渲染图片 let fileReader = new FileReader(); fileReader.onloadend = function (loadEvent) { $wrap.append( '<div class="pip">' + '<img class="imageThumb" src="' + loadEvent.target.result + '" title="' + file.name + '"/>' + '<br/><span class="remove"></span>' + "</div>" ); }; fileReader.readAsDataURL(file); } } // 更新input的files集合 this.files = dataTransfer.files; }); }); } else { alert("您的浏览器不支持File API"); } });
修复要点
- 事件委托:使用
$wrap.on("click", ".remove", function(){})代替重复绑定,只绑定一次事件,动态生成的.remove元素也能正常触发。 - 文件名匹配索引:删除时通过图片的
title(即文件名)找到对应在files_name_arr中的索引,确保精准删除目标文件。 - 重构文件添加逻辑:将文件读取和渲染放在添加新文件的循环中,避免重复遍历已有文件,提升代码效率。
- 移除重复绑定逻辑:删掉原代码中在
onloadend内对所有.remove元素的循环绑定,彻底解决点击一次触发多次的问题。
内容的提问来源于stack exchange,提问作者Duy Phi
相关产品推荐
相关产品推荐

