我想删除使用addEventListener()添加的所有特定类型的事件侦听器。我所看到的所有资源都在告诉你需要这样做:

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

但我希望能够在不知道它目前是什么情况下清除它,就像这样:

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

当前回答

在不引用原始函数的情况下删除事件侦听器的现代方法是使用AbortController。需要注意的是,您只能中止您自己添加的侦听器。

const buttonOne = document.querySelector('#button-one'); const buttonTwo = document.querySelector('#button-two'); const abortController = new AbortController(); // Add multiple click event listeners to button one buttonOne.addEventListener( 'click', () => alert('First'), { signal: abortController.signal } ); buttonOne.addEventListener( 'click', () => alert('Second'), { signal: abortController.signal } ); // Add listener to remove first button's listeners buttonTwo.addEventListener( 'click', () => abortController.abort() ); <p>The first button will fire two alert dialogs when clicked. Click the second button to remove those listeners from the first button.</p> <button type="button" id="button-one">Click for alerts</button> <button type="button" id="button-two">Remove listeners</button>

其他回答

你必须重写EventTarget.prototype.addEventListener来构建一个trap函数来记录所有“添加监听器”调用。就像这样:

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.removeEventListeners:

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

你不能删除一个事件,但是全部?在一次?只做

document.body.innerHTML = document.body.innerHTML

如果不拦截addEventListener调用并跟踪侦听器,或者使用允许这些功能的库,这是不可能的。如果监听器集合是可访问的,但没有实现该特性,则会出现这种情况。

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

注意:这也会删除element子元素上的监听器。

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

el.parentNode.replaceChild(elClone, el);

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

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

但是,请注意,这可能不适用于快速事件(如mouemove),因为事件的重新分派会引入延迟。

如果您需要这样做,最好的方法是首先跟踪添加的侦听器,正如Martin Wantke的回答中所概述的那样。

因此,这个函数将删除元素上的大部分指定侦听器类型:

function removeListenersFromElement(element, listenerType){
  const listeners = getEventListeners(element)[listenerType];
  let l = listeners.length;
  for(let i = l-1; i >=0; i--){
    removeEventListener(listenerType, listeners[i].listener);
  }
 }

有一些罕见的例外,其中一个不能删除的原因。