Javascript/DOM:如何删除 DOM 对象的所有事件侦听器?

IT技术 javascript events dom
2021-01-27 09:45:29

只是问题:有什么方法可以完全删除对象的所有事件,例如 div?

编辑:我正在添加每个div.addEventListener('click',eventReturner(),false);事件。

function eventReturner() {
    return function() {
        dosomething();
    };
}

EDIT2:我找到了一种有效的方法,但不能用于我的情况:

var returnedFunction;
function addit() {
    var div = document.getElementById('div');
    returnedFunction = eventReturner();
    div.addEventListener('click',returnedFunction,false); //You HAVE to take here a var and not the direct call to eventReturner(), because the function address must be the same, and it would change, if the function was called again.
}
function removeit() {
    var div = document.getElementById('div');
    div.removeEventListener('click',returnedFunction,false);
}
6个回答

我不确定您的意思是删除所有事件删除特定类型事件的所有处理程序还是一种类型的所有事件处理程序?

删除所有事件处理程序

如果您想删除所有事件处理程序(任何类型的),您可以克隆该元素并将其替换为它的克隆:

var clone = element.cloneNode(true);

注意:这将保留属性和子项,但不会保留对 DOM 属性的任何更改。


删除特定类型的“匿名”事件处理程序

另一种方法是使用,removeEventListener()但我猜你已经尝试过这个,但它没有用。这是捕获

addEventListener每次调用匿名函数都会创建一个新的侦听器。调用removeEventListener匿名函数无效匿名函数每次被调用时都会创建一个唯一的对象,尽管它可能会调用一个对象,但它不是对现有对象的引用。以这种方式添加事件侦听器时,请确保它只添加一次,它是永久的(不能删除),直到它被添加到的对象被销毁。

您本质上是将匿名函数传递给addEventListener作为eventReturner返回函数。

你有两种可能来解决这个问题:

  1. 不要使用返回函数的函数。直接使用函数:

     function handler() {
         dosomething();
     }
    
     div.addEventListener('click',handler,false);
    
  2. 创建一个包装器,用于addEventListener存储对返回函数的引用并创建一些奇怪的removeAllEvents函数:

     var _eventHandlers = {}; // somewhere global
    
     const addListener = (node, event, handler, capture = false) => {
       if (!(event in _eventHandlers)) {
         _eventHandlers[event] = []
       }
       // here we track the events and their nodes (note that we cannot
       // use node as Object keys, as they'd get coerced into a string
       _eventHandlers[event].push({ node: node, handler: handler, capture: capture })
       node.addEventListener(event, handler, capture)
     }
    
     const removeAllListeners = (targetNode, event) => {
       // remove listeners from the matching nodes
       _eventHandlers[event]
         .filter(({ node }) => node === targetNode)
         .forEach(({ node, handler, capture }) => node.removeEventListener(event, handler, capture))
    
       // update _eventHandlers global
       _eventHandlers[event] = _eventHandlers[event].filter(
         ({ node }) => node !== targetNode,
       )
     }
    

然后你可以使用它:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')

演示

注意:如果您的代码运行了很长时间并且您正在创建和删除大量元素,则必须确保在_eventHandlers销毁它们时删除其中包含的元素

@Florian:当然,代码可以改进,但它应该给你一个想法......快乐编码!
2021-03-13 09:45:29
@cstruter:是的……这是在我早期的 JavaScript 中……我会在找到更多时间时更正代码。谢谢你让我知道。
2021-03-24 09:45:29
您是否尝试过使用多个 div 元素进行此操作?节点 var 将实际转换为字符串,例如 '[object HTMLDivElement]' 这意味着您最终将所有内容添加到同一个节点。
2021-03-30 09:45:29
我猜有一个错字,addListener 应该是 addEvent
2021-04-05 09:45:29
请注意,这element.parentNode可能为空。应该在上面的那段代码周围放置一个 if 检查。
2021-04-06 09:45:29

这将从子级中删除所有侦听器,但对于大页面来说会很慢。写起来非常简单。

element.outerHTML = element.outerHTML;
@Ryan 而不是document.querySelector('body').outerHTML你可以输入document.body.outerHTML
2021-03-21 09:45:29
document.querySelector('body').outerHTML = document.querySelector('body').outerHTML 为我工作。
2021-03-28 09:45:29
不知道这是否是最好的方法,但它绝对是最简单的👌
2021-04-06 09:45:29
很好的答案。对于叶节点,这似乎是最好的主意。
2021-04-11 09:45:29

使用事件监听器自己的函数remove()例如:

getEventListeners().click.forEach((e)=>{e.remove()})
这似乎只适用于 chrome控制台即使在页面上的脚本上它也不起作用。
2021-03-12 09:45:29
getEventListeners(document).click.forEach((e)=>{e.remove()}) 产生这个错误: VM214:1 Uncaught TypeError: e.remove is not a function
2021-03-21 09:45:29
无法使其正常工作,您将对象作为参数传递给getEventListeners(). 例如:getEventListeners(document)getEventListeners(document.querySelector('.someclass'))
2021-03-29 09:45:29
似乎getEventListeners仅在 WebKit 开发工具和 FireBug 中可用。不是您可以在代码中调用的东西。
2021-04-02 09:45:29
不知道为什么这不是正确的答案。这个问题在 SO 上出现了很多次,并且可能会使用克隆方法来解决问题。
2021-04-05 09:45:29

正如 corwin.amber 所说,Webkit 与其他 Webkit 之间存在差异。

在 Chrome 中:

getEventListeners(document);

这为您提供了一个包含所有现有事件侦听器的对象:

Object 
 click: Array[1]
 closePopups: Array[1]
 keyup: Array[1]
 mouseout: Array[1]
 mouseover: Array[1]
 ...

从这里您可以访问要删除的侦听器:

getEventListeners(document).copy[0].remove();

所以所有的事件监听器:

for(var eventType in getEventListeners(document)) {
   getEventListeners(document)[eventType].forEach(
      function(o) { o.remove(); }
   ) 
}

在火狐浏览器中

有点不同,因为它使用不包含删除函数的侦听器包装器。您必须获得要删除的侦听器:

document.removeEventListener("copy", getEventListeners(document).copy[0].listener)

所有事件侦听器:

for(var eventType in getEventListeners(document)) {
  getEventListeners(document)[eventType].forEach(
    function(o) { document.removeEventListener(eventType, o.listener) }
  ) 
}

我偶然发现了这篇试图禁用新闻网站令人讨厌的复制保护的帖子。

享受!

这是一个糟糕的解决方案,因为目前 getEventListeners 仅受 Chrome 支持。
2021-03-19 09:45:29
这是针对删除侦听器的最佳答案,例如在删除文档节点上的特定侦听器而不删除其所有侦听器的情况下。
2021-04-03 09:45:29
这个工作正常,最好的答案。@Jmakuc 非常感谢。
2021-04-06 09:45:29
Firefox 告诉我getEventListeners未定义?
2021-04-08 09:45:29

您可以添加一个钩子函数来拦截对 的所有调用addEventHandler该钩子会将处理程序推送到可用于清理的列表。例如,

if (EventTarget.prototype.original_addEventListener == null) {
    EventTarget.prototype.original_addEventListener = EventTarget.prototype.addEventListener;

    function addEventListener_hook(typ, fn, opt) {
        console.log('--- add event listener',this.nodeName,typ);
        this.all_handlers = this.all_handlers || [];
        this.all_handlers.push({typ,fn,opt});
        this.original_addEventListener(typ, fn, opt);  
    }

    EventTarget.prototype.addEventListener = addEventListener_hook;
}

您应该在靠近主网页顶部的位置插入此代码(例如index.html)。在清理期间,您可以通过 all_handlers 循环,并为每个调用 removeEventHandler。不要担心使用相同的函数多次调用 removeEventHandler。它是无害的。

例如,

function cleanup(elem) {
    for (let t in elem) if (t.startsWith('on') && elem[t] != null) {
        elem[t] = null;
        console.log('cleanup removed listener from '+elem.nodeName,t);
    } 
    for (let t of elem.all_handlers || []) {
        elem.removeEventListener(t.typ, t.fn, t.opt);
        console.log('cleanup removed listener from '+elem.nodeName,t.typ);
    } 
}

注意:对于 IE,使用 Element 而不是 EventTarget,并将 => 更改为 function,以及其他各种内容。