ChatGPT解决这个技术问题 Extra ChatGPT

removeEventListener on anonymous functions in JavaScript

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.

Why do not save this function? Event handler may be not an anonymous function.
I realize this is a little late, but you can also use the 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.
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
Possible duplicate of JavaScript: remove event listener
@ Heretic Monkey this link is irrelevant: 1) it has no 'keydown' events and 2) all the answers include mouse clicking!

M
Micah Elliott

if you are inside the actual function, you can use arguments.callee as a reference to the function. as in:

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

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


This is nice since it preserves the advantages of anonymous functions (not polluting the namespace etc.).
tried this in WinJS app, got the next error: "Accessing the 'callee' property of an arguments object is not allowed in strict mode"
@ValentinKantor: That because something in the code there is a "use strict"; statement, and you cant use callee in strict mode.
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); });
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."
A
Adam Heath

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

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?
The this keyword can be confusing. A good place to read up on it is quirksmode.org/js/this.html
Thank you very much. This was most helpful.
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.
@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 developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
C
Community

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

Beautiful solution!
This may not be the right way but this is the easiest thing.
This worked in the context of a VueJS directive. I created a method and I only wanted it to run once.
I am curious why this might not be the right way
G
GavinBrelstaff

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


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.
with this polyfill for DOM Level 4 entries you should be fine npmjs.com/package/dom4
A
Ali Saeed
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


getEventListeners seems to be part of the Chrome Dev-tools, so isn't really usable for anything other than debugging.
Just tried it, confirmed that it is only available in the Devtools and not when included in a script inside a page.
C
Community

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()


@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
@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.
Got a good laugh out of this, very clever. Didn't seem to remove the load and submit handlers though.
C
Community

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

This is over complicated.
@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 :-)
Worked perfectly for me. I couldn't see another way to pass arguments into the function, as it couldn't be anonymous.
H
H K

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()

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. css-tricks.com/… There also exists a ponyfill/polyfill for AbortController: github.com/mo/abortcontroller-polyfill There's also a more thorough polyfill here github.com/mysticatea/event-target-shim
Y
Yash

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


J
Jonatas Walker

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

An explanation would be nice.
G
Gark Garcia

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.


S
Shay Connolly

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.


G
GROVER.

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

W
Wojtek Mazurek

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

A
Andrey

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)

s
spurushottam13

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()

Q
Qantas 94 Heavy
window.document.onkeydown = function(){};

Why not = undefined? Real badass.
This will not remove any handler that was registered with addEventListener.