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


97

只是一个问题:有什么办法可以完全删除某个对象(例如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);
}

您如何附加活动?
Felix Kling 2010年

2
标题询问对象的元素,而实际问题询问事件。您要删除子元素或事件吗?
维尔斯兰德(PärWieslander)2010年

哦,天哪,这是因为当我写那封信时,我一直在想别的事情……我很关心这些事件
FlorianMüller2010年

我不知道确切的情况,但是在某些情况下,您可以使用'on *'方法(如div.onclick = function),该方法始终与单个侦听器一起使用,并且很容易将其删除为div.onclick=null。当然,addEventListener在这种情况下,您不应该一起使用,因为它将添加一个独立于中的监听器onclick
venimus

Answers:


105

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

删除所有事件处理程序

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

var clone = element.cloneNode(true);

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


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

另一种方法是使用,removeEventListener()但我想您已经尝试过了,但是没有用。这是要抓住的地方

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

您实质上是将匿名函数传递给addEventListeneraseventReturner返回一个函数。

您可以通过两种方法解决此问题:

  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:当然,代码可以改进,但是应该可以给您一个想法……编码愉快!
Felix Kling 2010年

2
您是否尝试了多个div元素?实际上,节点var将转换为字符串,例如'[object HTMLDivElement]',这意味着您最终将所有内容添加到同一节点。
cstruter

@cstruter:是的……这是我早期使用JavaScript的方法……我将在发现更多时间后更正代码。谢谢你让我知道。
Felix Kling

我猜这里有一个错字,addListener应该是addEvent
AGamePlayer

请注意,该值element.parentNode可以为null。大概应该检查一下上面的那段代码。
thecoolmacdude

42

这将从儿童中删除所有侦听器,但是对于大页面而言会很慢。写得很残酷。

element.outerHTML = element.outerHTML;

2
好答案。对于叶节点,这似乎是最好的主意。
user3055938 '17

3
document.querySelector('body').outerHTML = document.querySelector('body').outerHTML为我工作。
瑞安

2
@Ryan代替document.querySelector('body').outerHTML您可以输入document.body.outerHTML
Jay Dadhania

28

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

getEventListeners().click.forEach((e)=>{e.remove()})

2
不知道为什么这不是正确的答案。这个问题在SO上出现了很多次,可能会使用clone方法回答解决问题。
jimasun

42
似乎getEventListeners仅在WebKit开发工具和FireBug中可用。没有什么可以直接在代码中调用的。
corwin.amber 16'December

1
在使该代码无法正常工作时遇到了麻烦,您将一个对象作为参数传递给getEventListeners()。例如:getEventListeners(document)getEventListeners(document.querySelector('.someclass'))
路加(Luke)

10
这似乎仅适用于chrome控制台。即使在页面上的脚本上也不起作用。
Reuel Ribeiro

4
getEventListeners(document).click.forEach((e)=>{e.remove()})产生此错误:VM214:1 Uncaught TypeError: e.remove is not a function
Ryan

11

正如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(); }
   ) 
}

在Firefox中

有所不同,因为它使用了不包含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) }
  ) 
}

我偶然发现了这篇文章,试图禁用新闻网站的烦人复制保护。

请享用!


这是针对删除侦听器的最佳答案,例如在删除文档节点上的特定侦听器而不删除其所有侦听器的情况下。
Trevor

这个工作很好,最好的答案。@Jmakuc非常感谢。
Thavamani Kasi

5
Firefox告诉我getEventListeners未定义吗?
罗维科

这是一个糟糕的解决方案,因为当前getEventListeners仅受Chrome支持。
Michael Paccione

1

您可以添加一个挂钩函数来拦截对的所有调用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以及其他各种功能。


0

为了完成答案,以下是一些实际示例,这些示例在您访问网站且无法控制生成的HTML和JavaScript代码时删除事件。

一些烦人的网站阻止您在登录表单上复制粘贴用户名,如果onpaste使用onpaste="return false"HTML属性添加了事件,则可以轻松绕过该用户名。在这种情况下,我们只需要右键单击输入字段,在Firefox之类的浏览器中选择“检查元素”,然后删除HTML属性。

但是,如果事件是通过JavaScript这样添加的:

document.getElementById("lyca_login_mobile_no").onpaste = function(){return false};

我们还必须通过JavaScript删除事件:

document.getElementById("lyca_login_mobile_no").onpaste = null;

在我的示例中,我使用ID“ lyca_login_mobile_no”,因为它是我所访问的网站使用的文本输入ID。

删除事件的另一种方法(也将删除所有事件)是删除节点并创建一个新节点,就像我们必须要做的addEventListener是使用匿名函数添加事件,而该匿名函数无法删除removeEventListener。也可以通过浏览器控制台检查元素,复制HTML代码,删除HTML代码,然后将HTML代码粘贴到同一位置,以完成此操作。

也可以通过JavaScript更快,更自动化地完成它:

var oldNode = document.getElementById("lyca_login_mobile_no");
var newNode = oldNode.cloneNode(true);
oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

更新:如果该Web应用是使用Angular之类的JavaScript框架制作的,则以前的解决方案似乎无法正常工作或破坏该应用。允许粘贴的另一种解决方法是通过JavaScript设置值:

document.getElementById("lyca_login_mobile_no").value = "username";

目前,我不知道是否有一种方法可以删除所有表单验证和限制事件,而又不会破坏完全用JavaScript(例如Angular)编写的应用程序。


0

角有这个问题的polyfill,你可以检查。我不太了解,但也许可以帮上忙。

const REMOVE_ALL_LISTENERS_EVENT_LISTENER ='removeAllListeners';

    proto[REMOVE_ALL_LISTENERS_EVENT_LISTENER] = function () {
        const target = this || _global;
        const eventName = arguments[0];
        if (!eventName) {
            const keys = Object.keys(target);
            for (let i = 0; i < keys.length; i++) {
                const prop = keys[i];
                const match = EVENT_NAME_SYMBOL_REGX.exec(prop);
                let evtName = match && match[1];
                // in nodejs EventEmitter, removeListener event is
                // used for monitoring the removeListener call,
                // so just keep removeListener eventListener until
                // all other eventListeners are removed
                if (evtName && evtName !== 'removeListener') {
                    this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, evtName);
                }
            }
            // remove removeListener listener finally
            this[REMOVE_ALL_LISTENERS_EVENT_LISTENER].call(this, 'removeListener');
        }
        else {
            const symbolEventNames = zoneSymbolEventNames$1[eventName];
            if (symbolEventNames) {
                const symbolEventName = symbolEventNames[FALSE_STR];
                const symbolCaptureEventName = symbolEventNames[TRUE_STR];
                const tasks = target[symbolEventName];
                const captureTasks = target[symbolCaptureEventName];
                if (tasks) {
                    const removeTasks = tasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
                if (captureTasks) {
                    const removeTasks = captureTasks.slice();
                    for (let i = 0; i < removeTasks.length; i++) {
                        const task = removeTasks[i];
                        let delegate = task.originalDelegate ? task.originalDelegate : task.callback;
                        this[REMOVE_EVENT_LISTENER].call(this, eventName, delegate, task.options);
                    }
                }
            }
        }
        if (returnTarget) {
            return this;
        }
    };

....


哪里是EVENT_NAME_SYMBOL_REGX从何而来?
埃里亚斯·扎马里亚

0

您可以添加一个帮助程序功能来清除事件监听器,例如

function clearEventListener(element) {
 const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}

只需将元素传递给函数就可以了...


-1
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

//编辑

我不知道您使用什么方法附加事件。为addEventListener您可以使用此:

div.removeEventListener('click',functionName,false); // functionName is the name of your callback function

更多 细节


1
如果我将其设置为类似,div.onClick = something则它将设置另一个事件onClick,但之前的事件仍然存在,因此我在上一个事件和一个事件(例如null)...
FlorianMüller2010年

1
如果通过添加处理程序,则将不起作用addEventListener()
Felix Kling 2010年

@弗洛里安:不,那不是真的。如果使用这种添加事件处理程序的方法,则一个事件只能有一个处理程序。但是,如果您使用,确实会有所不同addEventListener()...
Felix Kling 2010年

但是我已经看到了,所以,如果我再次添加上一个函数,它将被调用两次……而且,就像费利克斯·金说的那样,这不适用于addEventListener ...
FlorianMüller2010年

-1

如果您执行以下操作,则可能是浏览器会为您完成此操作:

复制div和及其属性,并将其插入旧属性之前,然后将内容从旧属性移到新属性,然后删除旧属性?


现在,这实际上是一个好主意,但完全不可行,如果您必须对1'000到1'000'000之间的大量div执行此操作,是的,这是一个大项目;)
FlorianMüller

6
每页100万个div?您将在此之前遇到其他麻烦;)然后可以使用事件委派...
麦克风2010年

-1

移除以下所有事件document

一班轮:

for (key in getEventListeners(document)) { getEventListeners(document)[key].forEach(function(c) { c.remove() }) }

漂亮版本:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}

-1

仅Chrome

当我尝试在量角器测试中删除EventListener且无法在测试中访问Listener时,以下工作将删除单个类型的所有事件侦听器:

function(eventType){
    getEventListeners(window).[eventType].forEach(
        function(e){
            window.removeEventListener(eventType, e.listener)
        }
    );
}

我希望这对以前的答案使用“删除”方法的人有所帮助,此方法从那时起不再起作用。


-1

您可以添加另一个事件侦听器并调用preventDefault()以停止先前侦听器的传播。

document.querySelectorAll('a.my-link').forEach((el) => {
  el.addEventListener('click', (ev) => {
    ev.preventDefault()
  })
})

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.