删除特定类型的所有事件侦听器

IT技术 javascript events
2021-01-15 21:41:29

我想删除使用addEventListener(). 我看到的所有资源都在说你需要这样做:

elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown',specific_function);

但我希望能够在不知道它当前是什么的情况下清除它,如下所示:

elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown');
6个回答

addEventListener不幸的是,如果不拦截调用并跟踪侦听器或使用允许此类功能的库,这是不可能的如果可以访问 listeners 集合但未实现功能,则情况会如此

您可以做的最接近的事情是通过克隆元素来删除所有侦听器,这不会克隆侦听器集合。

注意:这也将删除元素子元素上的侦听器。

var el = document.getElementById('el-id'),
    elClone = el.cloneNode(true);

el.parentNode.replaceChild(elClone, el);
我认为您假设被替换的节点(带有事件侦听器)将被垃圾收集。如果不是这种情况,您可能会遇到奇怪的问题。
2021-03-22 21:41:29
@Reno 孤立元素及其侦听器应在所有现代浏览器中进行垃圾收集。显然,如果您在 JS 中的某处保存了对初始 DOM 节点的一些引用,则必须考虑到这一点。
2021-03-24 21:41:29
这会删除所有侦听器,而不仅仅是特定类型的侦听器,因此从技术上讲,这不是该问题的可接受答案。除非你明确声明这是不可能的??!!
2021-03-24 21:41:29
另一个不好的地方是它会破坏对这个节点的引用。
2021-04-01 21:41:29
@Hector,window不是 DOM 元素,所以它不会。
2021-04-02 21:41:29

如果您删除监听器的唯一目标是阻止它们运行,您可以向窗口添加一个事件监听器,捕获并取消给定类型的所有事件:

window.addEventListener(type, function(event) {
    event.stopImmediatePropagation();
}, true);

传入true第三个参数会导致事件在下降过程中被捕获。停止传播意味着事件永远不会到达正在侦听它的侦听器。

但请记住,这用途非常有限,因为您无法为给定类型添加新的侦听器(它们都将被阻止)。有一些方法可以稍微解决这个问题,例如,通过触发一种只有您的听众知道要监听的新事件。您可以这样做:

window.addEventListener('click', function (event) {
    // (note: not cross-browser)
    var event2 = new CustomEvent('click2', {detail: {original: event}});
    event.target.dispatchEvent(event2);
    event.stopPropagation();
}, true);

element.addEventListener('click2', function(event) {
    if (event.detail && event.detail.original) {
        event = event.detail.original
    }
    // Do something with event
});

但是,请注意,这对于像 mousemove 这样的快速事件可能效果不佳,因为事件的重新调度会引入延迟。

如果您需要这样做,最好只跟踪首先添加的听众,如 Martin Wantke 的回答中所述。

如果事件绑定到 window 对象,比如 ie on message 事件怎么办?
2021-04-09 21:41:29

您必须覆盖 EventTarget.prototype.addEventListener 以构建用于记录所有“添加侦听器”调用的陷阱函数。像这样的东西:

var _listeners = [];

EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener)
{
    _listeners.push({target: this, type: type, listener: listener});
    this.addEventListenerBase(type, listener);
};

然后你可以构建一个 EventTarget.prototype.removeEventListener s

EventTarget.prototype.removeEventListeners = function(targetType)
{
    for(var index = 0; index != _listeners.length; index++)
    {
        var item = _listeners[index];

        var target = item.target;
        var type = item.type;
        var listener = item.listener;

        if(target == this && type == targetType)
        {
            this.removeEventListener(type, listener);
        }
    }
}

在 ES6 中,您可以使用 Symbol,直接在实例化对象 self 中隐藏原始函数和所有添加的侦听器列表。

(function()
{
    let target = EventTarget.prototype;
    let functionName = 'addEventListener';
    let func = target[functionName];

    let symbolHidden = Symbol('hidden');

    function hidden(instance)
    {
        if(instance[symbolHidden] === undefined)
        {
            let area = {};
            instance[symbolHidden] = area;
            return area;
        }

        return instance[symbolHidden];
    }

    function listenersFrom(instance)
    {
        let area = hidden(instance);
        if(!area.listeners) { area.listeners = []; }
        return area.listeners;
    }

    target[functionName] = function(type, listener)
    {
        let listeners = listenersFrom(this);

        listeners.push({ type, listener });

        func.apply(this, [type, listener]);
    };

    target['removeEventListeners'] = function(targetType)
    {
        let self = this;

        let listeners = listenersFrom(this);
        let removed = [];

        listeners.forEach(item =>
        {
            let type = item.type;
            let listener = item.listener;

            if(type == targetType)
            {
                self.removeEventListener(type, listener);
            }
        });
    };
})();

你可以用这个小狙击手测试这段代码:

document.addEventListener("DOMContentLoaded", event => { console.log('event 1'); });
document.addEventListener("DOMContentLoaded", event => { console.log('event 2'); });
document.addEventListener("click", event => { console.log('click event'); });

document.dispatchEvent(new Event('DOMContentLoaded'));
document.removeEventListeners('DOMContentLoaded');
document.dispatchEvent(new Event('DOMContentLoaded'));
// click event still works, just do a click in the browser

删除全局事件的所有侦听器

element.onmousedown = null;

现在您可以通过以下方式返回添加事件侦听器

element.addEventListener('mousedown', handler, ...);

此解决方案仅适用于“全局”事件。自定义事件将不起作用。以下是所有全球事件的列表:https : //developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers

我知道这是旧的,但我有一个没有真正答案的类似问题,我想keydown从文档中删除所有事件侦听器。我没有删除它们,而是addEventListener在添加它们之前覆盖忽略它们,类似于上面的 Toms 回答,通过在加载任何其他脚本之前添加它:

<script type="text/javascript">
    var current = document.addEventListener;
    document.addEventListener = function (type, listener) {
        if(type =="keydown")
        {
            //do nothing
        }
        else
        {
            var args = [];
            args[0] = type;
            args[1] = listener;
            current.apply(this, args);
        }
    };
</script>
您可以直接传递参数type === 'keydown' || current.apply(this, arguments);这将是一个很酷的单行程序。您还可以将整个内容包装在IIFE 中,以防止current泄漏到全局范围内。
2021-03-29 21:41:29