152

I have an object that has methods in it. These methods are put into the object inside an anonymous function. It looks like this:

var t = {};
window.document.addEventListener("keydown", function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
});  

(there is a lot more code, but this is enough to show the problem)

Now I want to stop the event listener in some cases. Therefore I am trying to do a removeEventListener but I can't figure out how to do this. I have read in other questions that it is not possible to call removeEventListener on anonymous functions, but is this also the case in this situation?

I have a method in t created inside the anonymous function and therefore I thought it was possible. Looks like this:

t.disable = function() {
    window.document.removeEventListener("keydown", this, false);
}

Why can't I do this?

Is there any other (good) way to do this?

Bonus info; this only has to work in Safari, hence the missing IE support.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
bitkid
  • 1,631
  • 2
  • 11
  • 4
  • Why do not save this function? Event handler may be not an anonymous function. – kirilloid Feb 09 '11 at 21:08
  • 2
    I realize this is a little late, but you can also use the [Node.setUserData](https://developer.mozilla.org/En/DOM/Node.setUserData)/Node.getUserData methods to store data about an element. For example, when you need to set an anon listener (and be able to remove it), first set userdata to an anon function `(Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);` and then do Elem.addEventListener('event', Elem.getUserData('eventListener'), false); ... and same for removeEventListener. Hope you can see this alright. – Chase Dec 28 '11 at 05:28
  • EDIT: As per previous comment, I guess that only works in Firefox...I just tried IE8 (IE9 unknown), Safari 5.1.2, Chrome (?), Opera 11..No dice – Chase Dec 28 '11 at 06:08
  • Possible duplicate of [JavaScript: remove event listener](https://stackoverflow.com/questions/4402287/javascript-remove-event-listener) – Heretic Monkey Jun 11 '19 at 14:13
  • @ Heretic Monkey this link is irrelevant: 1) it has **no 'keydown' events** and 2) all the answers include **mouse clicking**! – Apostolos Mar 06 '21 at 11:50

18 Answers18

132

You can name the function passed and use the name in the removeEventListener. as in:

button.addEventListener('click', function eventHandler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', eventHandler);
});

EDIT: This will not work if you are working in strict mode ("use strict";)

EDIT 2: arguments.callee is now deprecated (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/callee)

Community
  • 1
  • 1
Otto Nascarella
  • 2,445
  • 2
  • 17
  • 8
  • 2
    This is nice since it preserves the advantages of anonymous functions (not polluting the namespace etc.). – bompf Sep 07 '12 at 10:23
  • 4
    tried this in WinJS app, got the next error: "Accessing the 'callee' property of an arguments object is not allowed in strict mode" – Valentin Kantor Nov 15 '12 at 09:19
  • 1
    @ValentinKantor: That because something in the code there is a "use strict"; statement, and you cant use callee in strict mode. – OMA Mar 18 '13 at 04:24
  • In my case I wanted to run the `mousemove` listener constantly while `mousedown` was true, so placed two `if` statements within it, `(mouseDown)` and `(!mouseDown)` (where `mouseDown` was a boolean toggled on `mousedown` and `mouseup`), and placed `this.removeEvent...` inside of the `(!mouseDown)` if statement. Incredibly useful, thanks! – Zach Saucier Jul 05 '13 at 00:56
  • Perfect... Perfect... Perfect! THANK YOU! – sleepless_in_seattle Aug 23 '15 at 09:02
  • Even if you want to remove the "this", protecting your function about changing context you can use "event.target", something like: event.target.removeEventListener('click', arguments.callee); You should pass event to the anonymous function.. – Iran Reyes Nov 30 '15 at 21:51
  • 42
    Give the inline function a name and you can reference it without resorting to arguments.callee: `button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });` – Harry Love Feb 11 '16 at 00:56
  • 5
    As stated in Mozilla: "Warning: The 5th edition of ECMAScript (ES5) forbids use of arguments.callee() in strict mode. Avoid using arguments.callee() by either giving function expressions a name or use a function declaration where a function must call itself." – dude Apr 16 '16 at 07:14
  • 1
    To all those complaining about strict mode: in strict mode you can use `arguments.caller.callee`. – Protector one May 12 '16 at 09:08
  • Wow, this is fantastic! I was trying to have the ESC key hide a full screen div (then removeEventListener) and wasn't able to pass both event and an id through to my handler function. Ended up with what's below (which works like a charm)! – Chris Allinson Jul 17 '16 at 23:34
  • function addEscListenerToDocumentBody(id) { document.body.addEventListener('keypress', function (e) { if(e.keyCode === 27) { hideModal(id); document.body.removeEventListener('keypress', arguments.callee); } }); } – Chris Allinson Jul 17 '16 at 23:34
104

I believe that is the point of an anonymous function, it lacks a name or a way to reference it.

If I were you I would just create a named function, or put it in a variable so you have a reference to it.

var t = {};
var handler = function(e) {
    t.scroll = function(x, y) {
        window.scrollBy(x, y);
    };
    t.scrollTo = function(x, y) {
        window.scrollTo(x, y);
    };
};
window.document.addEventListener("keydown", handler);

You can then remove it by

window.document.removeEventListener("keydown", handler);   
Adam Heath
  • 4,703
  • 2
  • 35
  • 50
  • 3
    Thank you for your reply. I went with: var handler; window.document.addEventListener("keydown", handler = function(e) { But what I don't understand is why "this" does not reference the event listener. Shouldn't the event listener be an object? – bitkid Feb 12 '11 at 09:15
  • 2
    The `this` keyword can be confusing. A good place to read up on it is http://www.quirksmode.org/js/this.html – Adam Heath Feb 12 '11 at 23:23
  • Thank you very much. This was most helpful. – bitkid Feb 13 '11 at 08:37
  • I'm trying to do this to block a really persistent ad on a website. I know that this is the point of anonymous functions, but that doesn't mean that I wouldn't like to know how to do so. – Wyatt Ward Mar 05 '18 at 06:23
  • 1
    @bitkid: Inside a handler function (assuming it isn’t an arrow function), the `this` refers to the element that the listener is added to, not the event itself (which would be the parameter `e`). Therefore `this === e.currentTarget`. read https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#The_value_of_this_within_the_handler – chharvey Jun 05 '18 at 21:11
  • @AdamHeath is there a reason why I should use a variable instead of creating a named function (I think this would mean: `... addEventListener("keydown", function myName() { use e here ...`, wouldn't it?)? Maybe you want to show this solution in your post as code, since I first read over it because I didn't knew that you meant. But back to my question: Is using a variable faster? Or needs less memory space? ... (I personally add the same function to several buttons with different parameters) – user1 Jan 20 '20 at 09:42
  • @Adam Heath, Congrats! You solved a problem (a real "pain") that no one among dozens of people in here couldn't or thought they did!! And in a simplest way! – Apostolos Mar 06 '21 at 12:06
  • I can not understand why this is the accepted answer : obviously, if you use a named function(handler), you do not remove the event listener on an anonymous function. you don't answer the question at all. Otto Nascarella answer should be the accepted one. – Chrysotribax Dec 18 '21 at 11:24
93

A version of Otto Nascarella's solution that works in strict mode is:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});
Community
  • 1
  • 1
Melle
  • 7,639
  • 1
  • 30
  • 31
38

in modern browsers you can do the following...

button.addEventListener( 'click', () => {
    alert( 'only once!' );
}, { once: true } );

https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters

GavinBrelstaff
  • 3,016
  • 2
  • 21
  • 39
shunryu111
  • 5,895
  • 4
  • 27
  • 16
  • 1
    Cool until you find out that no version of IE nor edge < 16 actually support this feature. At least in 5 years we can use this since then IE will (read: should) be deprecated, Edge will have take its place and it will use the webkit engine instead of their own "EdgeHTML" thing. – SidOfc Dec 20 '18 at 11:46
  • 1
    with this polyfill for DOM Level 4 entries you should be fine https://www.npmjs.com/package/dom4 – shunryu111 Jan 07 '19 at 19:07
17

There's a new way to do this that is supported by the latest versions of most popular browsers with the exception of Safari.

Check caniuse for updated support.

Update: Now also supported by Sefari (version 15^).

We can add an option to addEventListner called signal and assign a signal from an AbortController on which you can later call the abort() method.

Here is an example.

We create an AbortController:

const controller = new AbortController();

Then we create the eventListner and pass in the option signal:

document.addEventListener('scroll',()=>{
    // do something
},{signal: controller.signal})

And then to remove the eventListner at a later time, we call:

controller.abort()
H K
  • 1,062
  • 8
  • 10
  • 2
    Didn't know about this, but it's pretty cool! Originally used to cancel a .fetch() request, AbortController can be used to remove an event listener. https://css-tricks.com/using-abortcontroller-as-an-alternative-for-removing-event-listeners/ There also exists a ponyfill/polyfill for AbortController: https://github.com/mo/abortcontroller-polyfill There's also a more thorough polyfill here https://github.com/mysticatea/event-target-shim – Mark Mar 24 '22 at 02:32
8
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

May be several anonymous functions, keydown1

Warning: only works in Chrome Dev Tools & cannot be used in code: link

Ali Saeed
  • 1,519
  • 1
  • 16
  • 23
  • 26
    `getEventListeners` seems to be part of the Chrome Dev-tools, so isn't really usable for anything other than debugging. – DBS Feb 01 '16 at 12:18
  • 1
    Just tried it, confirmed that it is only available in the Devtools and not when included in a script inside a page. – Andres Riofrio Mar 02 '18 at 01:58
6

This is not ideal as it removes all, but might work for your needs:

z = document.querySelector('video');
z.parentNode.replaceChild(z.cloneNode(1), z);

Cloning a node copies all of its attributes and their values, including intrinsic (in–line) listeners. It does not copy event listeners added using addEventListener()

Node.cloneNode()

Community
  • 1
  • 1
  • @AhmadAlfy Not really. Its confusing for future devs and likely will give bugs at some point. You should re-structure your code instead so this isn't needed – Martin Dawson Sep 10 '21 at 09:45
  • 2
    @MartinDawson I agree of course but it could be useful for some cases (unnamed callbacks added by a dependency for instance). It should be documented as well but it doesn't say it shouldn't be used. – Ahmad Alfy Sep 10 '21 at 14:17
  • Got a good laugh out of this, very clever. Didn't seem to remove the load and submit handlers though. – Devon Guerrero Nov 09 '21 at 23:47
2

A not so anonymous option

element.funky = function() {
    console.log("Click!");
};
element.funky.type = "click";
element.funky.capt = false;
element.addEventListener(element.funky.type, element.funky, element.funky.capt);
// blah blah blah
element.removeEventListener(element.funky.type, element.funky, element.funky.capt);

Since receiving feedback from Andy (quite right, but as with many examples, I wished to show a contextual expansion of the idea), here's a less complicated exposition:

<script id="konami" type="text/javascript" async>
    var konami = {
        ptrn: "38,38,40,40,37,39,37,39,66,65",
        kl: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
    };
    document.body.addEventListener( "keyup", function knm ( evt ) {
        konami.kl = konami.kl.slice( -9 );
        konami.kl.push( evt.keyCode );
        if ( konami.ptrn === konami.kl.join() ) {
            evt.target.removeEventListener( "keyup", knm, false );

            /* Although at this point we wish to remove a listener
               we could easily have had multiple "keyup" listeners
               each triggering different functions, so we MUST
               say which function we no longer wish to trigger
               rather than which listener we wish to remove.

               Normal scoping will apply to where we can mention this function
               and thus, where we can remove the listener set to trigger it. */

            document.body.classList.add( "konami" );
        }
    }, false );
    document.body.removeChild( document.getElementById( "konami" ) );
</script>

This allows an effectively anonymous function structure, avoids the use of the practically deprecated callee, and allows easy removal.

Incidentally: The removal of the script element immediately after setting the listener is a cute trick for hiding code one would prefer wasn't starkly obvious to prying eyes (would spoil the surprise ;-)

So the method (more simply) is:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );
Community
  • 1
  • 1
Fred Gandt
  • 4,217
  • 2
  • 33
  • 41
  • 2
    This is over complicated. – pronebird Sep 04 '14 at 10:30
  • @Andy I agree, kind of, but was trying to show that there is simply no way to remove an anonymous function. It *must* in some way be referenced (even callee (is bad, M'Kay) is referencing the function), and thus provided an example of just one (other) way the function can be referenced - and, how it's built of parts that can equally be stored for later reference (the important part). Obviously a truly anonymous function is somewhat built on-the-fly, and so knowing later which event action/type and whether capture was used must also be known. Anyway, here's a better method :-) – Fred Gandt Sep 05 '14 at 12:27
  • Worked perfectly for me. I couldn't see another way to pass arguments into the function, as it couldn't be anonymous. – nicodemus13 Oct 31 '14 at 14:03
2

To give a more up-to-date approach to this:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);
Jonatas Walker
  • 13,583
  • 5
  • 53
  • 82
  • 7
    An explanation would be nice. – Poul Bak May 14 '20 at 03:12
  • 1
    This seems to work because `addEventListener()` allows signature where second argument is an object with a function called `handleEvent()` and in this case `this` points to that object (see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#syntax). As this very same object was used to register the listener, the reference pointed by `this` is the correct one for removing the listener. If second argument of `addEventListener()` is a function (typical use) then `this` refers to the Event inside that function and this kind of trick cannot be used to remove the listener. – Mikko Rantalainen Sep 30 '22 at 08:38
1

JavaScript: addEventListener method registers the specified listener on the EventTarget(Element|document|Window) it's called on.

EventTarget.addEventListener(event_type, handler_function, Bubbling|Capturing);

Mouse, Keyboard events Example test in WebConsole:

var keyboard = function(e) {
    console.log('Key_Down Code : ' + e.keyCode);
};
var mouseSimple = function(e) {
    var element = e.srcElement || e.target;
    var tagName = element.tagName || element.relatedTarget;
    console.log('Mouse Over TagName : ' + tagName);    
};
var  mouseComplex = function(e) {
    console.log('Mouse Click Code : ' + e.button);
} 

window.document.addEventListener('keydown',   keyboard,      false);
window.document.addEventListener('mouseover', mouseSimple,   false);
window.document.addEventListener('click',     mouseComplex,  false);

removeEventListener method removes the event listener previously registered with EventTarget.addEventListener().

window.document.removeEventListener('keydown',   keyboard,     false);
window.document.removeEventListener('mouseover', mouseSimple,  false);
window.document.removeEventListener('click',     mouseComplex, false);

caniuse

Yash
  • 9,250
  • 2
  • 69
  • 74
1

I have stumbled across the same problem and this was the best solution I could get:

/*Adding the event listener (the 'mousemove' event, in this specific case)*/
element.onmousemove = function(event) {
    /*do your stuff*/
};
/*Removing the event listener*/
element.onmousemove = null;

Please keep in mind I have only tested this for the window element and for the 'mousemove' event, so there could be some problems with this approach.

Gark Garcia
  • 450
  • 6
  • 14
0

Possibly not the best solution in terms of what you are asking. I have still not determined an efficient method for removing anonymous function declared inline with the event listener invocation.

I personally use a variable to store the <target> and declare the function outside of the event listener invocation eg:

const target = document.querySelector('<identifier>');

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Then to remove the listener:

target.removeEventListener('click', myFunc);

Not the top recommendation you will receive but to remove anonymous functions the only solution I have found useful is to remove then replace the HTML element. I am sure there must be a better vanilla JS method but I haven't seen it yet.

0

I know this is a fairly old thread, but thought I might put in my two cents for those who find it useful.

The script (apologies about the uncreative method names):

window.Listener = {
    _Active: [],
    remove: function(attached, on, callback, capture){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            if(current[0] === attached && current[1] === on && current[2] === callback){
                attached.removeEventListener(on, callback, (capture || false));
                return this._Active.splice(i, 1);
            }
        }
    }, removeAtIndex(i){
        if(this._Active[i]){
            var remove = this._Active[i];
            var attached = remove[0], on = remove[1], callback = remove[2];
            attached.removeEventListener(on, callback, false);
            return this._Active.splice(i, 1);
        }
    }, purge: function(){
        for(var i = 0; i < this._Active.length; i++){
            var current = this._Active[i];
            current[0].removeEventListener(current[1], current[2]);
            this._Active.splice(i, 1);
        }
    }, declare: function(attached, on, callback, capture){
        attached.addEventListener(on, callback, (capture || false));
        if(this._Active.push([attached, on, callback])){
            return this._Active.length - 1;
        }
    }
};

And you can use it like so:

// declare a new onclick listener attached to the document
var clickListener = Listener.declare(document, "click" function(e){
    // on click, remove the listener and log the clicked element
    console.log(e.target);
    Listener.removeAtIndex(clickListener);
});

// completely remove all active listeners 
// (at least, ones declared via the Listener object)
Listener.purge();

// works exactly like removeEventListener
Listener.remove(element, on, callback);
GROVER.
  • 4,071
  • 2
  • 19
  • 66
0

I just experienced similiar problem with copy-protection wordpress plugin. The code was:

function disableSelection(target){
 if (typeof target.onselectstart!="undefined") //For IE 
  target.onselectstart=function(){return false}
 else if (typeof target.style.MozUserSelect!="undefined") //For Firefox
  target.style.MozUserSelect="none"
 else //All other route (For Opera)
  target.onmousedown=function(){return false}
target.style.cursor = "default"
}

And then it was initiated by loosely put

<script type="text/javascript">disableSelection(document.body)</script>.

I came around this simply by attaching other annonymous function to this event:

document.body.onselectstart = function() { return true; };
Wojtek Mazurek
  • 199
  • 3
  • 7
0

Set anonymous listener:

document.getElementById('ID').addEventListener('click', () => { alert('Hi'); });

Remove anonymous listener:

document.getElementById('ID').removeEventListener('click',getEventListeners(document.getElementById('ID')).click[0].listener)
Andrey
  • 345
  • 2
  • 7
0

Using the AbortController, neat and clean

Attaching EventListener

const el = document.getElementById('ID')
const controller = new AbortController;
el.addEventListener('click',() => {
  console.log("Clicked")
},{signal: controller.signal})

when you want to remove the event listener

controller.abort()
spurushottam13
  • 86
  • 2
  • 11
0

Another alternative workaround to achieve this is adding an empty event handler and preventing event propagation.

Let's assume you need to remove mouseleave event handler from an element which has #specific-div id, that is added with an anonymous function, and you can't use removeEventListener() since you don't have a function name.

You can add another event handler to that element and use event.stopImmediatePropagation(), for being sure this event handler works before existing ones you should pass the third parameter (useCapture) as true.

The final code should look like the below:

document.getElementById("specific-div")
   .addEventListener("mouseleave", function(event) {
      event.stopImmediatePropagation()
   }, true);

This could help for some specific cases that you can't prefer cloneNode() method.

Furkan Başaran
  • 1,927
  • 2
  • 19
  • 28
-4
window.document.onkeydown = function(){};
Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
Dun
  • 1