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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4386300/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 12:12:21  来源:igfitidea点击:

Javascript/DOM: How to remove all events of a DOM object?

javascripteventsdom

提问by Florian Müller

Just question: Is there any way to completely remove all events of an object, e.g. a div?

只是问题:有没有办法完全删除一个对象的所有事件,例如一个div?

EDIT: I'm adding per div.addEventListener('click',eventReturner(),false);an event.

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

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

EDIT2: I found a way, which is working, but not possible to use for my case:

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);
}

回答by Felix Kling

I am not sure what you mean with remove allevents. Remove all handlers for a specific type of event or all event handlers for one type?

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

Remove all event handlers

删除所有事件处理程序

If you want to remove all event handlers (of any type), you could clonethe element and replace it with its clone:

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

var clone = element.cloneNode(true);

Note:This will preserve attributes and children, but it will not preserve any changes to DOM properties.

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



Remove "anonymous" event handlers of specific type

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

The other way is to use removeEventListener()but I guess you already tried this and it didn't work. Here is the catch:

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

Calling addEventListenerto an anonymous function creates a new listener each time. Calling removeEventListenerto an anonymous function has no effect. An anonymous function creates a unique object each time it is called, it is not a reference to an existing object though it may call one. When adding an event listener in this manner be sure it is added only once, it is permanent (cannot be removed) until the object it was added to, is destroyed.

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

You are essentially passing an anonymous function to addEventListeneras eventReturnerreturns a function.

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

You have two possibilities to solve this:

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

  1. Don't use a function that returns a function. Use the function directly:

    function handler() {
        dosomething();
    }
    
    div.addEventListener('click',handler,false);
    
  2. Create a wrapper for addEventListenerthat stores a reference to the returned function and create some weird removeAllEventsfunction:

    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,
      )
    }
    

    And then you could use it with:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')
    
  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')
    

DEMO

演示

Note:If your code runs for a long time and you are creating and removing a lot of elements, you would have to make sure to remove the elements contained in _eventHandlerswhen you destroy them.

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

回答by pabombs

This will remove all listeners from children but will be slow for large pages. Brutally simple to write.

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

element.outerHTML = element.outerHTML;

回答by JuanGG

Use the event listener's own function remove(). For example:

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

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

回答by Jmakuc

As corwin.amber says, there are differences between Webkit an others.

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

In Chrome:

在 Chrome 中:

getEventListeners(document);

Which gives you an Object with all the existing event listeners:

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

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

From here you can reach the listener you want to remove:

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

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

So All the event listeners:

所以所有的事件监听器:

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

In Firefox

在火狐浏览器中

Is a little bit different because it uses a listener wrapper that contains no remove function. You have to get the listener you want to remove:

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

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

All the event listeners:

所有事件侦听器:

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

I stumbled with this post trying to disable the annoying copy protection of a news website.

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

Enjoy!

享受!

回答by baptx

To complete the answers, here are real-world examples of removing events when you are visiting websites and don't have control over the HTML and JavaScript code generated.

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

Some annoying websites are preventing you to copy-paste usernames on login forms, which could easily be bypassed if the onpasteevent was added with the onpaste="return false"HTML attribute. In this case we just need to right click on the input field, select "Inspect element" in a browser like Firefox and remove the HTML attribute.

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

However, if the event was added through JavaScript like this:

但是,如果事件是通过 JavaScript 添加的,如下所示:

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

We will have to remove the event through JavaScript also:

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

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

In my example, I used the ID "lyca_login_mobile_no" since it was the text input ID used by the website I was visiting.

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

Another way to remove the event (which will also remove all the events) is to remove the node and create a new one, like we have to do if addEventListenerwas used to add events using an anonymous function that we cannot remove with removeEventListener. This can also be done through the browser console by inspecting an element, copying the HTML code, removing the HTML code and then pasting the HTML code at the same place.

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

It can also be done faster and automated through JavaScript:

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

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

Update: if the web app is made using a JavaScript framework like Angular, it looks the previous solutions are not working or breaking the app. Another workaround to allow pasting would be to set the value through JavaScript:

更新:如果网络应用程序是使用像 Angular 这样的 JavaScript 框架制作的,看起来以前的解决方案不起作用或破坏了应用程序。允许粘贴的另一种解决方法是通过 JavaScript 设置值:

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

At the moment, I don't know if there is a way to remove all form validation and restriction events without breaking an app written entirely in JavaScript like Angular.

目前,我不知道是否有一种方法可以在不破坏像 Angular 这样完全用 JavaScript 编写的应用程序的情况下删除所有表单验证和限制事件。

回答by Filipe Melo

Chrome only

仅铬

As I'm trying to remove an EventListener within a Protractor test and do not have access to the Listener in the test, the following works to remove all event listeners of a single type:

由于我正在尝试删除量角器测试中的 EventListener 并且无法访问测试中的 Listener,因此以下操作可以删除单一类型的所有事件侦听器:

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

I hope this helps someone as previous answer were using the "remove" method which since then does not work anymore.

我希望这对某人有所帮助,因为之前的答案使用的是“删除”方法,此后该方法不再有效。

回答by feyzullah y?ld?z

angular has a polyfill for this issue, you can check. I did not understand much but maybe it can help.

angular 有针对此问题的 polyfill,您可以查看。我不太明白,但也许它可以帮助。

const REMOVE_ALL_LISTENERS_EVENT_LISTENER = 'removeAllListeners';

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[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;
        }
    };

....

....

回答by Rafal Enden

You could add another event listener and call preventDefault()to stop propagation of the previous listeners.

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

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

回答by Lacho Tomov

One method is to add a new event listener that calls e.stopImmediatePropagation().

一种方法是添加一个调用 e.stopImmediatePropagation() 的新事件侦听器。

回答by Ionu? Staicu

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

//edit

//编辑

I didn't knew what method are you using for attaching events. For addEventListeneryou can use this:

我不知道您使用什么方法来附加事件。因为addEventListener你可以使用这个:

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

moredetails

更多细节