240

I want to remove all event listeners of a specific type that were added using addEventListener(). All the resources I'm seeing are saying you need to do this:

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

But I want to be able to clear it without knowing what it is currently, like this:

elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown');
Remi Guan
  • 21,506
  • 17
  • 64
  • 87
J S
  • 3,324
  • 4
  • 20
  • 27

12 Answers12

279

That is not possible without intercepting addEventListener calls and keep track of the listeners or use a library that allows such features unfortunately. It would have been if the listeners collection was accessible but the feature wasn't implemented.

The closest thing you can do is to remove all listeners by cloning the element, which will not clone the listeners collection.

Note: This will also remove listeners on element's children.

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

el.parentNode.replaceChild(elClone, el);
Community
  • 1
  • 1
plalx
  • 42,889
  • 6
  • 74
  • 90
  • 6
    I think you're assuming that the replaced node (with event listeners) will be garbage collected. You might run into weird issues if that's not the case. – Renaud Nov 05 '14 at 15:25
  • 4
    @Reno Orphaned elements and their listeners should be garbage collected in all modern browsers. Obviously, if you held some references to the initial DOM node in JS somewhere, you will have to take that into account. – plalx Nov 05 '14 at 18:15
  • 1
    @Hector, `window` is not a DOM element so it wouldn't. – plalx May 08 '15 at 16:42
  • @plalx Why when we clone the node, the `event` is not cloned too? – Alston Jul 13 '15 at 09:46
  • @NazarVynnytskyi Yes that would basically do the same as cloning the children nodes. – plalx Jan 04 '17 at 15:04
  • Remove all listeners on element: element.parentNode.innerHTML += '' – Nazar Vynnytskyi Jan 17 '17 at 20:58
  • @NazarVynnytskyi That would be incorrect as it may affect other siblings. – plalx Jan 17 '17 at 20:59
  • 1
    Another bad thing about this is that it will break references to this node. – N73k Nov 03 '17 at 15:58
  • 1
    This removes all listeners not just those of a particular type, so technically is not an accepted answer for this question. Unless you declare plainly that it is not possible ??!! – user10089632 Nov 07 '17 at 17:53
  • 1
    @user10089632 It is not possible with native JS APIs. – plalx Nov 08 '17 at 12:50
  • So, can I assume that if a node is removed, using remove() or removeChild() or replaceChild() or even parentElement.innerHTML='' these actions will remove the node's event listeners too? What about its descendants node's event listeners? I don't want to clone it, just want to remove the node it and make sure all its listeners are disposed too. Thanks – cancerbero Feb 26 '19 at 16:29
107

If your only goal by removing the listeners is to stop them from running, you can add an event listener to the window capturing and canceling all events of the given type:

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

Passing in true for the third parameter causes the event to be captured on the way down. Stopping propagation means that the event never reaches the listeners that are listening for it.

Keep in mind though that this has very limited use as you can't add new listeners for the given type (they will all be blocked). There are ways to get around this somewhat, e.g., by firing a new kind of event that only your listeners would know to listen for. Here is how you can do that:

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

However, note that this may not work as well for fast events like mousemove, given that the re-dispatching of the event introduces a delay.

Better would be to just keep track of the listeners added in the first place, as outlined in Martin Wantke's answer, if you need to do this.

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Chris Middleton
  • 5,654
  • 5
  • 31
  • 68
27

You must override EventTarget.prototype.addEventListener to build an trap function for logging all 'add listener' calls. Something like this:

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

Then you can build an 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);
        }
    }
}

In ES6 you can use a Symbol, to hide the original function and the list of all added listener directly in the instantiated object 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);
            }
        });
    };
})();

You can test this code with this little snipper:

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
Martin Wantke
  • 4,287
  • 33
  • 21
16

Remove all listeners on a global event

element.onmousedown = null;

now you can go back to adding event listeners via

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

This solution only works on "Global" events. Custom events won't work. Here's a list of all global events: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers

Chris Hayes
  • 11,505
  • 6
  • 33
  • 41
  • I tried to use this method to clean onclick listeners from a button. But it did not work. So I went with removeEventListener() method. – Kanat Apr 15 '22 at 11:30
10

I know this is old, but I had a similar issue with no real answers, where I wanted to remove all keydown event listeners from the document. Instead of removing them, I override the addEventListener to ignore them before they were even added, similar to Toms answer above, by adding this before any other scripts are loaded:

<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>
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
PaladinMattt
  • 151
  • 1
  • 4
  • 2
    You can pass [arguments](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/arguments) directly, `type === 'keydown' || current.apply(this, arguments);` would be a cool one-liner. You can also wrap the whole thing in an [IIFE](https://developer.mozilla.org/docs/Glossary/IIFE) to prevent `current` leaking into the global scope. – Şafak Gür Dec 14 '18 at 07:21
10

A modern way to remove event listeners without referencing the original function is to use AbortController. A caveat being that you can only abort the listeners that you added yourself.

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>
Thomas Higginbotham
  • 1,662
  • 20
  • 25
5

Remove all listeners in element by one js line:

element.parentNode.innerHTML += '';
Nazar Vynnytskyi
  • 4,647
  • 2
  • 21
  • 22
4

In the extreme case of not knowing which callback is attached to a window listener, an handler can be wrapper around window addEventListener and a variable can store ever listeners to properly remove each one of those through a removeAllEventListener('scroll') for example.

var listeners = {};

var originalEventListener = window.addEventListener;
window.addEventListener = function(type, fn, options) {
    if (!listeners[type])
        listeners[type] = [];

    listeners[type].push(fn);
    return originalEventListener(type, fn, options);
}

var removeAllEventListener = function(type) {
    if (!listeners[type] || !listeners[type].length)
        return;

    for (let i = 0; i < listeners[type].length; i++)
        window.removeEventListener(type, listeners[type][i]);
}
yves amsellem
  • 7,106
  • 5
  • 44
  • 68
4

You cant remove a single event, but all? at once? just do

document.body.innerHTML = document.body.innerHTML

McKabue
  • 2,076
  • 1
  • 19
  • 34
  • I don't get why this is downvoted, the simplest one here –  May 11 '20 at 13:40
  • 7
    because this is a complete overkill, doing so you reset the whole page with all the possible side effects you might get – Flavien Volken Jul 28 '20 at 09:27
  • 1
    I continually get puzzled by fellas who don't understand a technology commenting about optimization... How can you optimize a technology/framework you don't understand? @FlavienVolken – McKabue Jul 28 '20 at 18:55
  • 7
    The question is "Remove All Event Listeners of Specific Type", your solution would remove all the listener of the page of any type. – Flavien Volken Jul 29 '20 at 05:54
2

So this function gets rid of most of a specified listener type on an element:

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

There have been a few rare exceptions where one can't be removed for some reason.

Mike Sraj
  • 81
  • 1
  • 4
  • By far the best answer! Why didn't anyone mention `getEventListeners` earlier? – miu Apr 16 '20 at 20:28
  • 15
    I'm sorry, but I have to take back what I said, `getEventListeners` is only working in ChromeDevTools from the command line, therefore it's not useful for almost any case. https://codepen.io/azaslavsky/pres/sybfE – miu Apr 16 '20 at 21:48
1

You could alternatively overwrite the 'yourElement.addEventListener()' method and use the '.apply()' method to execute the listener like normal, but intercepting the function in the process. Like:

<script type="text/javascript">

    var args = [];
    var orginalAddEvent = yourElement.addEventListener;

    yourElement.addEventListener = function() {
        //console.log(arguments);
        args[args.length] = arguments[0];
        args[args.length] = arguments[1];
        orginalAddEvent.apply(this, arguments);
    };

    function removeListeners() {
        for(var n=0;n<args.length;n+=2) {
            yourElement.removeEventListener(args[n], args[n+1]);
        }
    }

    removeListeners();

</script>

This script must be run on page load or it might not intercept all event listeners.

Make sure to remove the 'removeListeners()' call before using.

Tom Burris
  • 380
  • 2
  • 19
0
 var events = [event_1, event_2,event_3]  // your events

//make a for loop of your events and remove them all in a single instance

 for (let i in events){
    canvas_1.removeEventListener("mousedown", events[i], false)
}
Nagama Inamdar
  • 2,851
  • 22
  • 39
  • 48
Sri Harsha
  • 33
  • 6