如何删除元素中的所有监听器?

317

我有一个按钮,并且给它添加了一些事件监听器

document.getElementById("btn").addEventListener("click", funcA, false);
document.getElementById("btn").addEventListener("click", funcB, false);
document.getElementById("btn").addEventListener("click", funcC, false);
document.getElementById("btn").addEventListener("blur" , funcD, false);
document.getElementById("btn").addEventListener("focus", funcE, false);

<button id="btn">button</button>

我可以通过以下方式将它们移除:

document.getElementById("btn").removeEventListener("click",funcA);

如果我想一次性删除所有监听器,或者我没有函数引用(funcA),该怎么办?是否有一种方法可以这样做,还是我必须一个一个地删除它们?


https://dev59.com/h3A75IYBdhLWcg3wkZ-A - Misiur
110
因为试图避免使用框架/库而努力编写代码,我给你点赞。 :-) - John
2
@user 令人印象深刻,你居然找到了一个比这个老问题还要老的问题,在这里的答案仍然提到jQuery 1.7。简直不敢相信自己问这个问题以来已经过去了这么长时间。 - Derek 朕會功夫
3个回答

318

我认为最快的方法是克隆节点,这将删除所有事件侦听器:

var old_element = document.getElementById("btn");
var new_element = old_element.cloneNode(true);
old_element.parentNode.replaceChild(new_element, old_element);

只需小心,因为这也会清除有关节点的所有子元素上的事件侦听器,因此,如果您想保留它们,就必须一个接一个地显式删除侦听器。


74
@Derek: 克隆一个节点和整个子树是一个不好的主意。这比使用 node.removeEventListener 从节点中删除所有 EventListener 要慢得多。此外,您将会产生内存泄漏(节点+子树),而且当然也会将子树中的所有 EventListener 都删除。如果您在 document.body 上使用您的函数,那么一切都会被炸掉。 - Saxoier
7
@Saxoier 克隆节点的速度比仅删除监听器慢,但在大多数功能场景中,速度差异不会明显(除非您一次对许多页面节点执行此操作)。至于内存泄漏,这将取决于浏览器...所有现代浏览器应该能够处理垃圾回收,以避免出现问题(尽管如果节点包含嵌入式对象,我可以想象可能会发生这种情况的场景)。您有特定已记录的内存泄漏问题吗? - Ben D
1
@Ben D:看起来最新版本的Firefox、IE、Opera和Chrome都有不错的垃圾回收(在10个GC周期后总结)。在某些情况下,旧版IE会出现内存泄漏问题。 - Saxoier
26
不喜欢神秘的布尔类型参数的人,cloneNode(true) 的意思是克隆包括子节点在内的节点。文档链接:https://developer.mozilla.org/en-US/docs/Web/API/Node.cloneNode - Andrew Dunkman
5
有趣,这里有三个踩但没有其他备选答案……那些踩的人表现得相当无力! - AJP
显示剩余14条评论

59
如果你正在使用jQuery事件,这可以在一行中完成:
对于jQuery事件(.on()):
$("#myEl").off()

对于本地 JavaScript 事件(.addEventListener()):

$('#myEl').replaceWith($('#myEl').clone());

这里有一个例子:

http://jsfiddle.net/LkfLezgd/3/


2
太长了,相反地在jQuery中你应该这样做 $("#myEl").unbind();.off()(1.7+) 来移除所有的监听器。 - Derek 朕會功夫
33
off()unbind() 只会移除通过 jQuery 添加的监听器吗? - davide
5
дёҚзҹҘйҒ“дёәд»Җд№Ҳ unbind е’Ң off дёҚиө·дҪңз”ЁгҖӮ然иҖҢ $('#myEl').replaceWith($('#myEl').clone()); еҫҲеҘҪз”ЁпјҒ - AGamePlayer
35
根据jQuery文档,对于使用原生JavaScript的addEventListener注册的监听器,off()unbind()将不起作用。 - argaz
5
看起来这只会删除使用jQuery创建的事件,因此并不是完整的解决方案。 - Nathan B
显示剩余6条评论

27

这是一个基于cloneNode的函数,但它有一个选项只克隆父节点并移动所有子节点(以保留它们的事件监听器):

function recreateNode(el, withChildren) {
  if (withChildren) {
    el.parentNode.replaceChild(el.cloneNode(true), el);
  }
  else {
    var newEl = el.cloneNode(false);
    while (el.hasChildNodes()) newEl.appendChild(el.firstChild);
    el.parentNode.replaceChild(newEl, el);
  }
}

移除一个元素上的事件监听器:

recreateNode(document.getElementById("btn"));

从一个元素及其所有子元素中移除事件监听器:

recreateNode(document.getElementById("list"), true);
如果您需要保留对象本身,因此无法使用cloneNode,那么您必须包装 addEventListener 函数并自行跟踪侦听器列表,例如在此答案中所示。

1
@Max:感谢您的编辑建议,但我认为将这段代码重构为ES2015并不适用于这种情况,因为那样的话这段代码就无法被广泛支持了。 - user
4
为了删除所有子元素的监听器,您可以使用以下代码:element.innerHTML += ''; 请注意,此操作将清空该元素中的所有子元素以及它们的事件监听器。 - Nazar Vynnytskyi
@NazarVynnytskyi 确实,那是一个快速而有效的解决方案! - Yes Barry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接