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

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

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

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

编辑2: 我找到了一种方法,这种方法很有效,但不能用于我的案子:

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);
}
184320 次浏览
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

更多 < a href = “ https://developer.mozilla.org/en/DOM/element.RemoveEventListener”rel = “ nofollow”> 细节

如果你这样做的话,浏览器可能会帮你做到这一点:

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

我不知道你说的 删除 < strong > all 事件是什么意思。删除特定类型事件的所有处理程序还是删除一种类型的所有事件处理程序?

删除所有事件处理程序

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

var clone = element.cloneNode(true);

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


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

另一种方法是使用 removeEventListener(),但我猜你已经尝试过了,它不起作用。 这就是问题所在:

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

eventReturner返回一个函数时,您实际上是将一个匿名函数传递给 addEventListener

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

  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中包含的元素。

这将删除子级中的所有侦听器,但是对于大页面来说速度会很慢。

element.outerHTML = element.outerHTML;

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

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

正如 corwin.amber 所说,Webkit 和其他工具有不同之处。

在 Chrome 中:

getEventListeners(document);

它为您提供了一个包含所有现有事件侦听器的 Object:

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 中

有一点不同,因为它使用不包含删除函数的侦听器包装器。你必须找到你想要移除的听众:

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

我在这篇文章中试图禁用一个新闻网站恼人的复制保护功能。

好好享受吧!

移除 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()
})
}

为了完成这些问题的答案,这里有一些真实的例子,当你访问网站并且无法控制生成的 HTML 和 JavaScript 代码时,删除事件。

一些恼人的网站阻止您在登录表单上复制粘贴用户名,如果使用 onpaste="return false" HTML 属性添加 onpaste事件,则可以很容易地绕过这些复制粘贴。 在这种情况下,我们只需要右键单击输入字段,在像 Firefox 这样的浏览器中选择“ Inspectelement”,然后删除 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。

更新2: 还有一种方法可以删除一个特定的事件,这个事件是在一个我们并不拥有的网站上通过使用 getEventListeners功能与 removeEventListener相结合而添加的,就像 Jmakuc 的答案中提到的那样。如果 getEventListeners不像 Firefox 那样存在,您可以使用 polyfill 并在页面上注入 Greasemonkey addon: https://github.com/colxi/getEventListeners/issues/1脚本

其中一个方法是添加一个新的事件侦听器,该侦听器调用 e.stop 即时传播()。

有一个角填充这个问题,你可以检查。我不明白很多,但也许它可以帮助。

Const REMOVE _ ALL _ LISTENERS _ EVENT _ LISTENER = ‘ RemoveAllListener’;

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

....

您可以添加一个 hook 函数来拦截对 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 _ handler,并为每个处理程序调用 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,并且 change = > 为 function,以及其他各种事情。

例如,可以添加一个帮助器函数来清除事件侦听器

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

只要把元素传递给函数就可以了。

您可以添加功能,并删除所有其他点击分配他们

btn1 = document.querySelector(".btn-1")
btn1.addEventListener("click" , _=>{console.log("hello")})
btn1.addEventListener("click" , _=>{console.log("How Are you ?")})


btn2 = document.querySelector(".btn-2")
btn2.onclick = _=>{console.log("Hello")}
btn2.onclick = _=>{console.log("Bye")}
<button class="btn-1">Hello to Me</button>
<button class="btn-2">Hello to Bye</button>

您确实可以通过克隆节点来删除所有事件处理程序,就像@FelixKling 在他的回答中建议的那样,但是不要忘记这一点

属性事件处理程序不受克隆的影响

有这样的元素

<div id="test" onclick="alert(42)">test</div>

克隆后仍会有点击警报。要删除此类事件,通常需要使用 removeAttribute方法

const removeAttEvents = el =>
[...el.attributes].forEach(att =>
att.name.startsWith("on") && el.removeAttribute(att.name)
);

然后使用上面的 test元素,调用 removeAttEvents(test)可以摆脱 click 处理程序。

克隆元素并将其替换为克隆元素。不克隆事件。

elem.replaceWith(elem.cloneNode(true));

这将使用 CloneNode ()克隆 elem DOM 对象,该对象将忽略所有事件处理程序(不过,正如 Jan Turo 的回答所指出的,类似 onclick="…"的属性将保留)。然后它使用 替换为()用该克隆替换 elem。给一个匿名克隆人的简单任务对我不起作用。

这应该比重新定义 elem.outerHTML本身(正如 帕博姆斯的回答提出的)更快、更简洁,但可能比重复遍历和清除每个监听器的答案更慢(注意,getEventListeners()似乎只能在 Chrome 的开发控制台中使用,不能在 Chrome 的其他地方使用,也不能在 Firefox 上使用)。据推测,如果要清除的侦听器数量增加,这种非循环解决方案就会变得更快。

(这是在 Ashoto 的评论的帮助下对 菲利克斯 · 克林的回答进行的简化。)

来自 JavaScriptWebAPI 的 EventTarget 的子类。支持在不指定处理程序函数引用的情况下删除事件。

class SmartEventTarget extends EventTarget {
constructor() {
super();
this.handlers = {};
}
addEventListener(name, handler) {
super.addEventListener(name, handler);
if (!this.handlers[name]) {
this.handlers[name] = new Set();
}
this.handlers[name].add(handler);
}
removeEventListener(name, handler) {
if (handler) {
super.removeEventListener(name, handler);
this.handlers[name].delete(handler);
} else {
this.handlers[name].forEach(h => {
super.removeEventListener(name, h)
});
this.handlers[name].clear();
}
}
removeAllListeners(name) {
if (name) {
this.removeEventListener(name, null);
} else {
Object.keys(this.handlers).map(name => {
this.removeEventListener(name, null);
});
this.handlers = {};
}
}
}

有关单元测试,请参见此 Gist。只需将 Gist 中的代码复制到浏览器 JS 控制台并按回车键,即可运行测试。

在盲目粘贴到控制台之前,一定要阅读来自互联网的奇怪 JS。

Https://gist.github.com/angstyloop/504414aba95b61b98be0db580cb2a3b0

我知道这是一个古老的问题,但对我来说,唯一奏效的是:

parentOfTheElement.innerHTML = parentOfTheElement.innerHTML;

虽然其他解决方案实际上删除了所有侦听器,但是在使用 outerHTML技巧或 cloneNode()时,我在添加新的侦听器时遇到了问题