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

143

仅仅一个问题:有没有办法完全移除对象的所有事件,例如一个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);
}

你如何附加事件? - Felix Kling
2
标题询问对象的“元素”,而实际问题询问的是“事件”。您想要删除子元素还是事件? - Pär Wieslander
哦,该死,那是因为我在写那个的时候想着其他事情了...我关心的是事件。 - Florian Müller
1
我不确定确切的情况,但在某些情况下,作为解决办法,您可以使用“on *”方法(例如div.onclick = function),它始终与单个监听器一起工作,并且很容易通过 div.onclick=null 进行移除。当然,在这种情况下,您不应该使用 addEventListener,因为它会添加一个不同于 onclick 中的监听器。 - venimus
也许更优雅的解决方案是简单地扩展EventTarget,覆盖其addEventListener和removeEventListener方法以维护包含处理程序函数引用的数据结构。请参见Johns和angstyloop的解决方案,了解此方法的示例。在angsyloop的评论中链接的完整Gist中有测试,您可以在浏览器控制台中运行它们。在运行之前,请先阅读奇怪的JS。 - angstyloop
19个回答

1
你可以添加一个帮助函数,例如清除事件监听器。
function clearEventListener(element) {
 const clonedElement = element.cloneNode(true);
element.replaceWith(clonedElement);
return clonedElement;
}

只需将元素传递给函数,就完成了...


0

JavaScript WebAPI 中 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


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)的情况下删除所有表单验证和限制事件。

更新2:还有一种方法可以通过使用getEventListeners函数结合removeEventListener来删除在我们不拥有的网站上使用addEventListener添加的特定事件,如Jmakuc的答案中所述。如果像Firefox这样不存在getEventListeners,则可以使用polyfill并使用Greasemonkey附加组件将脚本注入页面:https://github.com/colxi/getEventListeners/issues/1


0

如果你做了类似以下的操作,浏览器可能会自动完成:

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


现在这个想法确实不错,但如果你需要在1,000到1,000,000个div之间执行此操作,那么它的性能会非常低下...是的,这是一个大项目 ;) - Florian Müller
8
在一个页面中有1百万个div?在遇到这个问题之前,你可能会遇到其他麻烦 ;) 可以考虑使用事件委托来解决... - Mic

0

Angular有一个针对这个问题的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 应该从哪里来? - Elias Zamaria
预定义常量框架 - Wil

-1

好的,我同意。也许我来晚了。或者我的情况不同。

所以,我试图移除元素上的事件监听器;换句话说,我不希望用户在那个元素上“点击”

如果有人仅仅想要阻止“点击”一个元素,最简单的方法是将CSS属性“pointer-events”设置为“none”

在我的情况下,我想要禁用以下内容的点击。 enter image description here

所以我所做的就是

.air-datepicker-nav--title {
    pointer-events: none;
}

提醒一下,我展示的库是https://air-datepicker.com/

我在这里添加答案以回应上述情况。请不要因为它不是js而给出负面评价。


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

-2

删除所有在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()
  })   
}

-3
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... - Florian Müller
1
如果通过 addEventListener() 添加处理程序,则无法工作。 - Felix Kling
@Florian:不,那不是真的。如果你使用这种方法添加事件处理程序,你只能有一个处理程序来处理一个事件。但是如果你使用addEventListener(),就会有所不同…… - Felix Kling
但我看到过这样的情况,如果我再次添加之前的函数,它将被调用两次...就像Felix King所说的那样,这与addEventListener不兼容... - Florian Müller

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