删除匿名事件监听器

72

有没有办法删除像这样添加的事件监听器:

element.addEventListener(event, function(){/* do work here */}, false);

不替换元素?


6
如果有多个匿名监听者,你该如何选择正确的一个进行移除? - Sean Hogan
1
@Sean Hogan:什么?我不明白你问的与我的问题有什么关系。 - erikvold
1
@Sean:我认为意图是从事件处理程序中删除事件监听器。 - icktoofay
14个回答

37

如果没有在创建时存储事件处理程序的引用,就没有办法干净地删除事件处理程序。

通常我会将这些添加到该页面上的主对象中,然后您可以迭代并在使用完该对象后进行清理。


10
我想知道为什么addeventlistener不能返回一个这样的引用,以供将来使用removeeventlistener。 - Michael
2
@Joe,对于来晚的用户:addEventListener在某些情况下返回undefined。例如,在BroadcastChannel上下文中。 - BairDev
1
@Joe 当然可以。更新官方接受的答案可能会很好,这样未来寻找参考资料的人(就像我一样)就会知道新的进展。 - Jason Gross
我已经有五年左右没有进行过大量的JavaScript开发了。我很乐意让别人编辑一下以使其更加正确。我现在更多地从事后端和嵌入式工作,对现代JavaScript不太熟悉了。虽然那个链接提到它只能在控制台中使用,并且请求是要通过编程方式实现的。我不知道这方面是否有所改变。 - Joe
对于未来的读者,@OllieWilliams的回答可能会有用:https://dev59.com/BnA75IYBdhLWcg3wy8Qi#72673200 - Mike Willis
显示剩余3条评论

26

您可以按照以下方法移除事件监听器:

element.addEventListener("click", function clicked() {
    element.removeEventListener("click", clicked, false);
}, false);

8
只有当你首先添加事件监听器并且可以修改原始 listener() 代码时,才能这样做。由于这是一个 Greasemonkey 应用程序,所以这是不可能的。即使基本页面脚本被更改,你只会添加另一个匿名侦听器。 - Brock Adams
1
@Brock:哦,我以为你的意图是要删除你添加的监听器。我不知道有什么方法可以删除你没有添加并且没有函数引用的监听器。 - icktoofay
Brock是正确的,这是一个用户脚本编写的问题,但回答很好,我给了它一个赞! - erikvold
1
你可能想要添加的是,callee 自 ES5 起已被弃用(请参见 mdnSO)。 - merours
@fxm:谢谢。我不记得我最初为什么使用了arguments.callee,而命名函数表达式应该可以很好地工作,所以我编辑了我的答案。 - icktoofay
clicked 函数在其自身内部可见吗?我遇到了一些问题:https://dev59.com/67L3oIgBc1ULPQZFTXIK - shen

21

这是一个老问题,但这里有一个解决方案。

严格来说,如果没有存储函数的引用,你无法移除匿名事件侦听器。由于使用匿名函数的目标可能不是创建一个新变量,因此你可以将引用存储在元素本身中:

element.addEventListener('click',element.fn=function fn() {
    //  Event Code
}, false);

稍后,当您想要将其删除时,可以执行以下操作:

element.removeEventListener('click',element.fn, false);

请记住,第三个参数(false)必须与添加事件侦听器时的值相同。

然而,这个问题本身又引出了另一个问题:为什么?

使用 .addEventListener() 而不是更简单的 .onsomething() 方法有两个原因:

首先,它允许添加多个事件侦听器。当需要选择性地删除它们时会变成一个问题:你可能最终需要对它们进行命名。如果要全部删除,那么 @tidy-giant 的 outerHTML 解决方案是很好的选择。

其次,您可以选择捕获而不是冒泡事件。

如果这两个原因都不重要,那么您可能会决定使用更简单的 onsomething 方法。


1
我会使用这个解决方案。 不确定是否重要,但之后仍将引用该函数,例如调用element.fn()仍将返回函数的结果。如果没有清理,这可能会成为内存问题吗? - Jarrod McGuire

20

匿名 绑定 事件监听器

删除元素所有事件监听器最简单的方法是将其outerHTML赋值为自身。这样做会通过HTML解析器发送HTML的字符串表示,并将解析后的HTML分配给元素。因为没有传递JavaScript,所以不会有绑定的事件监听器。

document.getElementById('demo').addEventListener('click', function(){
    alert('Clickrd');
    this.outerHTML = this.outerHTML;
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>

匿名委托事件监听器

唯一的例外是委托事件监听器,或者是在父元素上监听符合一组标准的每个子元素事件的事件监听器。要避免这种情况,唯一的方法是更改元素以不符合委托事件监听器的条件。


document.body.addEventListener('click', function(e){
    if(e.target.id === 'demo') {
        alert('Clickrd');
        e.target.id = 'omed';
    }
}, false);
<a id="demo" href="javascript:void(0)">Click Me</a>


8

是的,您可以删除匿名事件监听器:

const controller = new AbortController();

document.addEventListener(
  "click",
  () => {
    // do function stuff
  },
  { signal: controller.signal }
);

您可以像这样删除事件监听器:
controller.abort();

5
您可以尝试覆盖 element.addEventListener 并做任何您想做的事情。
类似这样:
var orig = element.addEventListener;

element.addEventListener = function (type, listener) {
    if (/dontwant/.test(listener.toSource())) { // listener has something i dont want
        // do nothing
    } else {
        orig.apply(this, Array.prototype.slice.apply(arguments));
    }
};

备注:虽然不建议这样做,但它可以解决问题(未经测试)


或者你可以替换 EventTarget.prototype.addEventListener。这对我有用。 - Lorcan O'Neill

3

使用文字函数分配事件处理程序是棘手的- 不仅没有办法删除它们,而且必须克隆节点并用克隆替换它才能删除- 您还可能会意外多次分配相同的处理程序,如果使用处理程序的引用则不会发生此情况。两个函数始终被视为两个不同的对象,即使它们是字符完全相同的。


“你不能”总结起来就是我认为的正确答案。 - erikvold
你可以将它们分配为属性,而不是使用addEventListener。element.onmouseover=function(){dothis}; element.onmouseover=''; - kennebec
2
在Greasemonkey应用程序中,我们无法控制基本页面中事件的创建方式。请注意,在GM脚本中,类似于element.onmouseover=function的语句无法工作,因为存在沙盒保护机制。请参阅http://commons.oreilly.com/wiki/index.php/Greasemonkey_Hacks/Getting_Started#Pitfall_.232:_Event_Handlers。 - Brock Adams

2
编辑:根据Manngo的建议,您应该使用.off()而不是.unbind(),因为自jQuery 3.0起,.unbind()已被弃用并自jQuery 1.7以来已被取代。

尽管这是一个旧问题,并且它没有提到jQuery,但我会在这里发布我的答案,因为它是搜索词'jquery remove anonymous event handler'的第一个结果。
您可以尝试使用.off()函数删除它。

$('#button1').click(function() {
       alert('This is a test');
});

$('#btnRemoveListener').click(function() {
       $('#button1').off('click');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button1">Click me</button>
<hr/>
<button id="btnRemoveListener">Remove listener</button>

然而,这仅适用于使用jQuery添加了监听器的情况 - 而不是使用.addEventListener。在此处找到了这个信息。

2
回到问题本身,jQuery实际上会存储对函数的引用,然后再添加该函数。这就是为什么您不需要指定要删除的“哪个”函数。也就是说,在添加时它不是匿名的。顺便说一句,jQuery更喜欢使用.off()而不是.unbind() - Manngo
1
为什么要在JS问题中给出jQuery答案? - ortonomy

1
//get Event
let obj = window; //for example
let eventStr= "blur"; //for example
let index= 0; //you can console.log(getEventListeners(obj)[eventStr]) and check index
let e = getEventListeners(obj)[eventStr][index];
//remove this event
obj .removeEventListener(eventStr,e.listener,e.useCapture);

结束了 :)

我在Chrome 92中测试过,可以工作


只有 Chrome 支持在命令行上执行此操作 - 它已被放弃。 - Bravo

1
如果您正在使用jQuery,请尝试使用off方法。
$("element").off("event");

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