点击图片触发文件选择,取消后再选择图片导致多次AJAX请求重复执行的问题求助
我的问题场景
我做了一个带图片列的表格,每行的图片点击后会调用同一个函数,触发隐藏的文件选择框选图;选完图片后用croppie.js打开模态框裁剪,裁剪结果会放到一个隐藏的预览图里,最后通过预览图的load事件触发AJAX提交更新。
但现在遇到了一个棘手的问题:如果我点击第一张图打开文件选择框后点取消,再点击第二张图同样取消选择,第三次点击第三张图选择图片并完成裁剪后,前两次的AJAX请求居然也跟着执行了,结果三张图片都被更新成了同一张选择的图片!
我排查后发现,问题应该出在每次点击图片时,都会给预览图绑定一次load事件,但这些旧的事件没有被清理,一直累积着;等第三次预览图加载完成时,所有绑定过的load事件就一起触发了,导致重复请求。
我想要的效果是:每次点击图片只处理当前这次的操作,如果取消选择就彻底终止流程,不会影响其他点击的请求。
下面是我的相关代码:
触发选择的核心函数
function livePictureEdit(element) { let id = parseInt( document.getElementById(event.target.id).getAttribute("data-id") ); let uuid = document.getElementById(event.target.id).getAttribute("data-uuid"); let type = document.getElementById(event.target.id).getAttribute("data-type"); let action = document .getElementById(event.target.id) .getAttribute("data-action"); let value = ""; // 我怀疑这里就是事件堆叠的根源 $("#student_picture_preview").on("load", function () { value = $("#student_picture_preview").attr("src"); // 提交更新的AJAX函数 commitLiveEdit(id, uuid, value, type, action); }); }
每行的点击触发标签(表格渲染时生成,每行的label都绑定了同一个点击事件)
<label for="student_picture" id="livePictureEdit_<?php echo $data["id"]?>" onclick="livePictureEdit(this);" data-type="student" data-uuid="<?php echo $_SESSION["csrfToken"]; ?>" data-action="picture" data-id="<?php echo $data["id"]; ?>"> </label>
页面底部的共用隐藏元素
<input type="file" id="student_picture" style="display: none" accept="image/jpeg, image/png"> <img id="student_picture_preview" class="liveEdit p-0 m-0" style="display: none;">
解决思路和方案
这个问题的核心就是重复绑定的事件没有被清理——每次点击图片都给预览图新增了一个load事件监听器,旧的监听器没被移除,新的又不断叠加,次数多了就导致事件堆积。
给你两个最直接的解决办法:
绑定新事件前先移除旧事件
每次绑定新的load事件前,先用off方法把之前所有的load事件都清除,这样就只会保留当前这次的监听器:// 替换原来的事件绑定代码 $("#student_picture_preview").off("load").on("load", function () { value = $("#student_picture_preview").attr("src"); commitLiveEdit(id, uuid, value, type, action); });使用一次性事件绑定
jQuery的one方法可以绑定一个只会执行一次的事件,事件触发后会自动解绑,完全不用担心事件累积的问题,代码也更简洁:// 用one替代on,事件仅触发一次后自动解绑 $("#student_picture_preview").one("load", function () { value = $("#student_picture_preview").attr("src"); commitLiveEdit(id, uuid, value, type, action); });
另外还有个小优化建议:你函数里用event.target.id去查找元素,但其实函数参数已经传入了element,直接用element获取属性更高效,不用重复查询DOM:
function livePictureEdit(element) { let id = parseInt(element.getAttribute("data-id")); let uuid = element.getAttribute("data-uuid"); let type = element.getAttribute("data-type"); let action = element.getAttribute("data-action"); // ... 后续代码 }
如果担心预览图缓存导致load事件不触发,还可以在函数开头重置一下预览图的src:
$("#student_picture_preview").attr("src", "");
这样应该就能彻底解决重复AJAX请求的问题啦!
备注:内容来源于stack exchange,提问作者Ivan C.

