jQuery解绑事件处理器失效:动态添加DOM节点后事件无法解绑
我一眼就看出问题所在啦——你每次执行$('[data-abc="tester"]').on('click', redoClick)时,是给所有匹配该选择器的元素(包括之前已经添加的旧div)重复绑定了redoClick事件。哪怕你先执行了off操作,每次绑定还是会覆盖旧绑定后再给所有元素重新挂一遍handler,次数多了旧元素的事件就会堆积,点击一次触发多次逻辑。
而且针对动态添加的元素,更推荐用事件委托从根源上避免这类问题,下面给你几种可行的解决方案:
方案1:使用事件委托(最推荐)
事件委托是把监听器绑定在一个已存在的父元素上(比如body或者更接近的容器),利用事件冒泡机制处理动态元素的事件。不管你什么时候添加新div,都能自动触发事件,无需手动反复绑定/解绑,也不会出现事件堆积。
示例代码:
$(function() { var theTXT = '<div data-abc="tester">click here</div>'; // 给#loader绑定点击事件,负责添加新元素 $("#loader").on('click', function() { $("body").append(theTXT); }); // 用事件委托给所有[data-abc="tester"]绑定点击逻辑 $("body").on('click', '[data-abc="tester"]', redoClick); function redoClick() { // 这里写你的点击处理逻辑 console.log('tester div被点击了'); } });
如果之后需要解绑所有这类事件,只需要执行:
$("body").off('click', '[data-abc="tester"]', redoClick);
一次性移除委托的监听器,不会有残留问题。
方案2:仅给新添加的元素绑定事件
如果你不想用委托,那每次添加新div时,只针对刚创建的这个元素绑定事件,而非全局选择所有匹配元素,这样旧元素就不会被重复绑定:
$(function() { var theTXT = '<div data-abc="tester">click here</div>'; $("#loader").on('click', function() { // 先创建元素,再单独给它绑定事件,最后添加到DOM var newDiv = $(theTXT); newDiv.on('click', redoClick); $("body").append(newDiv); }); function redoClick() { console.log('新的tester div被点击了'); } });
这里的关键是操作刚创建的newDiv,而非全局选择器,从根源避免了给旧元素重复绑定的问题。
原代码问题复盘
你原代码里每次点击#loader都会执行:
$('[data-abc="tester"]').off('click', redoClick); $('[data-abc="tester"]').on('click', redoClick);
这会先移除所有匹配元素的redoClick事件,紧接着又给所有匹配元素(包括旧的)重新绑定一次。点击#loaderN次,旧元素就会被绑定N次redoClick,点击旧div时就会触发N次逻辑,这就是事件堆积的原因。
总结下,优先用事件委托,代码更简洁、性能更好,也能彻底规避这类绑定问题;如果非要单独绑定,记得只操作新创建的元素哦!
内容的提问来源于stack exchange,提问作者Thomas Mavrofides

