139

Just question: Is there any way to completely remove all events of an object, e.g. a div?

EDIT: I'm adding per div.addEventListener('click',eventReturner(),false); an event.

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

EDIT2: I found a way, which is working, but not possible to use for my case:

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);
}
S.Serpooshan
  • 7,608
  • 4
  • 33
  • 61
Florian Müller
  • 7,448
  • 25
  • 78
  • 120
  • How do you attach the events? – Felix Kling Dec 08 '10 at 10:14
  • 2
    The title asks about the **elements** of the object, while the actual question asks about the **events**. Do you want to remove the child elements or the events? – Pär Wieslander Dec 08 '10 at 10:17
  • oh d*mn, that was cause i've been thinking at something else when i wrote that... i'm caring about the _events_ – Florian Müller Dec 08 '10 at 10:18
  • 1
    I don't know the exact case but in some cases as a workaround you could use the 'on*' methods (as div.onclick = function), which always works with a single listener and is easy to remove as `div.onclick=null`. Of course you should not use `addEventListener` in this case altogether as it will add a separate listener different from the one in `onclick`. – venimus Mar 14 '17 at 09:19
  • A perhaps more elegant solution is to simply extend EventTarget, overriding its addEventListener and removeEventListener methods to maintain a data structure containing your handler function references. See Johns' and angstyloop's solutions below for examples of this approach. There are tests in the full Gist linked in angsyloop's comment, which you can run in your browser console. Please read strange JS before running it in your browser console. – angstyloop Nov 02 '22 at 06:45

19 Answers19

153

I am not sure what you mean with remove all events. Remove all handlers for a specific type of event or all event handlers for one type?

Remove all event handlers

If you want to remove all event handlers (of any type), you could clone the element and replace it with its clone:

var clone = element.cloneNode(true);

Note: This will preserve attributes and children, but it will not preserve any changes to DOM properties.


Remove "anonymous" event handlers of specific type

The other way is to use removeEventListener() but I guess you already tried this and it didn't work. Here is the catch:

Calling addEventListener to an anonymous function creates a new listener each time. Calling removeEventListener to an anonymous function has no effect. An anonymous function creates a unique object each time it is called, it is not a reference to an existing object though it may call one. When adding an event listener in this manner be sure it is added only once, it is permanent (cannot be removed) until the object it was added to, is destroyed.

You are essentially passing an anonymous function to addEventListener as eventReturner returns a function.

You have two possibilities to solve this:

  1. Don't use a function that returns a function. Use the function directly:

     function handler() {
         dosomething();
     }
    
     div.addEventListener('click',handler,false);
    
  2. Create a wrapper for addEventListener that stores a reference to the returned function and create some weird removeAllEvents function:

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

And then you could use it with:

    addListener(div, 'click', eventReturner(), false)
    // and later
    removeAllListeners(div, 'click')

DEMO

Note: If your code runs for a long time and you are creating and removing a lot of elements, you would have to make sure to remove the elements contained in _eventHandlers when you destroy them.

Giampaolo Ferradini
  • 529
  • 1
  • 6
  • 17
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • @Florian: Certainly, the code can be improve, but it should give you an idea... happy coding! – Felix Kling Dec 08 '10 at 12:32
  • 2
    Have you tried this with more than one div element? the node var will actually be converted to a string, e.g. '[object HTMLDivElement]' which means you end up adding everything to the same node. – cstruter Feb 29 '12 at 16:18
  • @cstruter: Right... this was in my early days of JavaScript... I will correct the code when I find some more time. Thanks for letting me know. – Felix Kling Feb 29 '12 at 16:21
  • I guess there's a typo, addListener should be addEvent – AGamePlayer Mar 01 '16 at 02:56
  • Note that `element.parentNode` could be null. Should probably put an if check around that piece of code above. – thecoolmacdude Apr 18 '17 at 15:22
  • You forgot to empty _eventHandlers after removing the listeners: ```delete _eventHandlers[node]; ``` – Simon Backx May 08 '17 at 12:27
  • _"An anonymous function creates a unique object each time it is called"_ is misleading. It would be correct to say that a _function literal_ creates a new function object every time it is evaluated, which is not the same thing as calling the function. The latter has no such unexpected side-effects, and you can avoid new function objects being created by storing the one you want in a variable / property. This allows a listener, created from a function literal, to be removed for example. Also, the "declaration style" statement "function foo(){}" acts like assigning a literal to a variable. – Hugh Allen Sep 10 '19 at 12:02
  • How can I replace a cloned node properly? I want to get rid of a click event on the video on the front page of Twitch.tv, I tried this but it breaks the video. `e=document.querySelectorAll("div.tw-flex-grow-1.tw-full-height.tw-relative")[1]; p=e.parentNode; c=e.cloneNode(true); e.remove(); p.appendChild(c);` – Shayan Dec 03 '19 at 18:28
  • this answer is genius – AGrush Jan 14 '20 at 15:14
  • Is there a workaround for the problem of node being converted to String? As mentioned by @cstruter, the addListener and removeAllListeners doesn't work properly because it ends up hashing all elements of the same type in the same list. – Elias Dorneles May 09 '20 at 16:19
  • 4
    `var clone = element.cloneNode(true); element.replaceWith(clone);` – asheroto Mar 17 '22 at 08:27
  • A perhaps more elegant solution is to simply extend EventTarget, overriding its addEventListener and removeEventListener methods to maintain a data structure containing your handler function references. See Johns' and angstyloop's solutions below for examples of this approach. There are tests in the full Gist linked in angsyloop's comment, which you can run in your browser console. Please read strange JS before running it in your browser console. – angstyloop Nov 02 '22 at 06:45
70

This will remove all listeners from children but will be slow for large pages. Brutally simple to write.

element.outerHTML = element.outerHTML;
pabombs
  • 1,380
  • 2
  • 14
  • 15
  • 3
    Great answer. For leaf nodes, this seems to be the best idea. – user3055938 Nov 25 '17 at 10:07
  • 5
    `document.querySelector('body').outerHTML = document.querySelector('body').outerHTML` worked for me. – Ryan Oct 13 '18 at 18:15
  • 7
    @Ryan instead of `document.querySelector('body').outerHTML` you can just type `document.body.outerHTML` – Jay Dadhania Jan 17 '20 at 00:48
  • even simpler: element.outerHTML += ""; Papercuts: variable reference to this element will continue to point to the original DOM node. It's replaced by removing `element` from the document, then inserting the new one. So for example with jQuery const link = $('#link'); link[0].outerHTML+=""; link.attr("href","/x.html"); changes the href of the node in the jQuery Collection, but that's not the one in the page! And const will then suck for refreshing the collection lol. – Wil Aug 01 '23 at 17:43
28

Use the event listener's own function remove(). For example:

getEventListeners().click.forEach((e)=>{e.remove()})
RJHunter
  • 2,829
  • 3
  • 25
  • 30
JuanGG
  • 834
  • 7
  • 11
  • 2
    Not sure why this is not the right answer. The question appears so many times on SO and may answers handle the problem using the clone method. – jimasun Nov 14 '16 at 13:48
  • 63
    It seems that `getEventListeners` is only available in the WebKit dev tools and in FireBug. Not something you can just call in your code. – corwin.amber Dec 01 '16 at 17:08
  • 1
    had trouble getting this to work, you pass an object as an argument to `getEventListeners()`. example: `getEventListeners(document)` or `getEventListeners(document.querySelector('.someclass'))` – Luke Jan 03 '17 at 19:02
  • I tried this with Chrome... Brilliant. This works phenomenally! Thanks JuanGG. – Travis J Mar 14 '17 at 04:59
  • 12
    This seems to only work on chrome *console*. Even on your script on page it does not work. – Reuel Ribeiro Nov 20 '17 at 17:03
  • 6
    `getEventListeners(document).click.forEach((e)=>{e.remove()})` produces this error: `VM214:1 Uncaught TypeError: e.remove is not a function` – Ryan Oct 13 '18 at 18:13
  • @Ryan try this: `getEventListeners(document).click.forEach((e)=>{e.listener.remove()})` – Shayan Dec 03 '19 at 19:15
  • 7
    Only supported in Chrome actually. Since this is not cross browser it is a poor solution. – Michael Paccione May 31 '20 at 06:32
18

As corwin.amber says, there are differences between Webkit an others.

In Chrome:

getEventListeners(document);

Which gives you an Object with all the existing event listeners:

Object 
 click: Array[1]
 closePopups: Array[1]
 keyup: Array[1]
 mouseout: Array[1]
 mouseover: Array[1]
 ...

From here you can reach the listener you want to remove:

getEventListeners(document).copy[0].remove();

So All the event listeners:

for(var eventType in getEventListeners(document)) {
   getEventListeners(document)[eventType].forEach(
      function(o) { o.remove(); }
   ) 
}

In Firefox

Is a little bit different because it uses a listener wrapper that contains no remove function. You have to get the listener you want to remove:

document.removeEventListener("copy", getEventListeners(document).copy[0].listener)

All the event listeners:

for(var eventType in getEventListeners(document)) {
  getEventListeners(document)[eventType].forEach(
    function(o) { document.removeEventListener(eventType, o.listener) }
  ) 
}

I stumbled with this post trying to disable the annoying copy protection of a news website.

Enjoy!

Jmakuc
  • 313
  • 2
  • 5
8

Clone the element and replace the element with its clone. Events are not cloned.

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

This uses Node.cloneNode() to clone the elem DOM object, which ignores all event handlers (though, as Jan Turoň's answer notes, attributes like onclick="…" will remain). It then uses Element.replaceWith() to replace elem with that clone. Simple assignment to an anonymous clone wasn't working for me.

This should be faster and cleaner than redefining elem.outerHTML with itself (as proposed by pabombs's answer) but may be slower than answers that iterate through and purge each listener (noting that getEventListeners() seems available exclusively in Chrome's dev console—not elsewhere in Chrome, not at all on Firefox). Presumably, at some higher volume of listeners to clear, this non-loop solution becomes faster.

(This is a simplification of Felix Kling's answer with help from asheroto's comment to it.)

phyatt
  • 18,472
  • 5
  • 61
  • 80
Adam Katz
  • 14,455
  • 5
  • 68
  • 83
5

You can add a hook function to intercept all calls to addEventHandler. The hook will push the handler to a list that can be used for cleanup. For example,

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

You should insert this code near the top of your main web page (e.g. index.html). During cleanup, you can loop thru all_handlers, and call removeEventHandler for each. Don't worry about calling removeEventHandler multiple times with the same function. It is harmless.

For example,

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

Note: for IE use Element instead of EventTarget, and change => to function, and various other things.

John Henckel
  • 10,274
  • 3
  • 79
  • 79
  • My approach below is similar to John. The idea is to subclass EventTarget, overriding its addEventListener and removeEventListener methods, while keeping handler functions in a data structure, like John did. In the full Gist, linked in my comment, I also added some unit tests. It could always use more, so feedback is appreciated. – angstyloop Nov 02 '22 at 06:42
4

you can add function and remove all other click by assign them

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>
hossein sedighian
  • 1,711
  • 1
  • 13
  • 16
2

You can indeed remove all event handlers by cloning the node as @FelixKling suggests in his answer, however don't forget that

attribute event handlers are not affected by cloning

Having element like this

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

will still alert on click after cloning. To remove this sort of events, you need to use removeAttribute method, in general

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

Then having the test element above, calling removeAttEvents(test) gets rid of the click handler.

Jan Turoň
  • 31,451
  • 23
  • 125
  • 169
2

The only easy way I found and worked is this:

Let's say we want to add 2 event listeners

const element = document.getElementById("element");
element.addEventListener('mouseover', 
  ()=>{ 
     // some task
  });

element.addEventListener('mouseout', 
  ()=>{ 
     // some task
  });

Now you can remove both of the listeners by simply:

element.replaceWith(element.cloneNode(true));
1

To complete the answers, here are real-world examples of removing events when you are visiting websites and don't have control over the HTML and JavaScript code generated.

Some annoying websites are preventing you to copy-paste usernames on login forms, which could easily be bypassed if the onpaste event was added with the onpaste="return false" HTML attribute. In this case we just need to right click on the input field, select "Inspect element" in a browser like Firefox and remove the HTML attribute.

However, if the event was added through JavaScript like this:

document.getElementById("lyca_login_mobile_no").onpaste = function(){return false};

We will have to remove the event through JavaScript also:

document.getElementById("lyca_login_mobile_no").onpaste = null;

In my example, I used the ID "lyca_login_mobile_no" since it was the text input ID used by the website I was visiting.

Another way to remove the event (which will also remove all the events) is to remove the node and create a new one, like we have to do if addEventListener was used to add events using an anonymous function that we cannot remove with removeEventListener. This can also be done through the browser console by inspecting an element, copying the HTML code, removing the HTML code and then pasting the HTML code at the same place.

It can also be done faster and automated through JavaScript:

var oldNode = document.getElementById("lyca_login_mobile_no");
var newNode = oldNode.cloneNode(true);
oldNode.parentNode.insertBefore(newNode, oldNode);
oldNode.parentNode.removeChild(oldNode);

Update: if the web app is made using a JavaScript framework like Angular, it looks the previous solutions are not working or breaking the app. Another workaround to allow pasting would be to set the value through JavaScript:

document.getElementById("lyca_login_mobile_no").value = "username";

At the moment, I don't know if there is a way to remove all form validation and restriction events without breaking an app written entirely in JavaScript like Angular.

Update 2: There is also a way to remove a specific event that was added with addEventListener on a website we don't own, by using the getEventListeners function combined to removeEventListener like mentioned in the answer of Jmakuc. If getEventListeners does not exist like on Firefox, you can use a polyfill and inject the script on the page with Greasemonkey addon: https://github.com/colxi/getEventListeners/issues/1

baptx
  • 3,428
  • 6
  • 33
  • 42
1

You can add a helper function that clears event listener for example

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

just pass in the element to the function and that's it...

Abhilash Narayan
  • 157
  • 2
  • 14
1

I know this is an old question but for me the only thing that worked was:

parentOfTheElement.innerHTML = parentOfTheElement.innerHTML;

While the other solutions do in fact remove all the listeners, I had problems adding new ones when using either the outerHTML trick or cloneNode()

GlassBeaver
  • 196
  • 4
  • 15
  • The problem with outerHTML and cloneNode methods is that you work with the node itself, and when it is replaced, your variable still points to the unreplaced node. You can verify this with an input element ie "" const f = foo; f.outerHTML+=""; f.value="baz"; console.log(foo.value, f.value) "bar baz" to you need to re-acquire the node after replacing it. This same problem applies to collections ie jQuery const e = $('#foo'); e[0].outerHTML+=""; e.val('baz'); foo.value // "bar" – Wil Aug 01 '23 at 17:29
0

May be the browser will do it for you if you do something like:

Copy the div and its attributes and insert it before the old one, then move the content from the old to the new and delete the old?

Mic
  • 24,812
  • 9
  • 57
  • 70
  • now that is actually a good idea, but totally inperformant, if you have to do this for an amound of divs between 1'000 and 1'000'000... Yes, it's a big project ;) – Florian Müller Dec 08 '10 at 10:29
  • 8
    1M divs in a page? You will get into other troubles before this one ;) May be use event delegation then... – Mic Dec 08 '10 at 20:02
0

angular has a polyfill for this issue, you can check. I did not understand much but maybe it can help.

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

....

feyzullahyildiz
  • 456
  • 4
  • 11
0

Sub-class of EventTarget from the JavaScript WebAPI. Supports removing events without specifying a handler function reference.

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

See this Gist for unit tests. You can run the tests by simply copying the code from the Gist into your browser JS console and pressing enter.

Be sure to read strange JS from the internet before blindly pasting it into your console.

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

angstyloop
  • 117
  • 6
0

Alright, I agree. Maybe I am late for the party. Or my scenario is different.

So I was trying to remove the event listener on the element; in other words, I didn't want the user to "Click" on that element.

If anyone simply wants to prevent a "Click" on an element, the easiest way was setting the CSS property "pointer-events" to "none"

In my case, I wanted to disable clicking on the following. enter image description here

So all I did was

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

FYI, The library I demoed above is https://air-datepicker.com/

I am just adding the answer here for the above scenario. Please don't put negative just because it's not js .

amjad1233
  • 21
  • 7
-1

One method is to add a new event listener that calls e.stopImmediatePropagation().

LachoTomov
  • 3,312
  • 30
  • 42
-2

Removing all the events on document:

One liner:

for (key in getEventListeners(document)) { getEventListeners(document)[key].forEach(function(c) { c.remove() }) }

Pretty version:

for (key in getEventListeners(document)) {
  getEventListeners(document)[key].forEach(function(c) {
    c.remove()
  })   
}
Dorian
  • 22,759
  • 8
  • 120
  • 116
-2
var div = getElementsByTagName('div')[0]; /* first div found; you can use getElementById for more specific element */
div.onclick = null; // OR:
div.onclick = function(){};

//edit

I didn't knew what method are you using for attaching events. For addEventListener you can use this:

div.removeEventListener('click',functionName,false); // functionName is the name of your callback function

more details

Ionuț Staicu
  • 21,360
  • 11
  • 51
  • 58
  • 1
    if i set it like `div.onClick = something` then it sets another event onClick, but the previous stays, so i have on previous and one for example null... – Florian Müller Dec 08 '10 at 10:17
  • 1
    Will not work if handlers are added via `addEventListener()`. – Felix Kling Dec 08 '10 at 10:17
  • @Florian: No that is not true. If you use this method of adding an event handler, you can only have **one** handler for an event. But it does make a difference if you use `addEventListener()`... – Felix Kling Dec 08 '10 at 10:18
  • but i've seen it like this, so if i added the previous function again, it will be called twice... and, like Felix King says, this does not work with addEventListener... – Florian Müller Dec 08 '10 at 10:20