removeEventListener sulle funzioni anonime in JavaScript


102

Ho un oggetto che contiene metodi. Questi metodi vengono inseriti nell'oggetto all'interno di una funzione anonima. Assomiglia a questo:

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

(c'è molto più codice, ma questo è sufficiente per mostrare il problema)

Ora voglio fermare il listener di eventi in alcuni casi. Quindi sto cercando di fare un removeEventListener ma non riesco a capire come farlo. Ho letto in altre domande che non è possibile chiamare removeEventListener su funzioni anonime, ma è così anche in questa situazione?

Ho un metodo in t creato all'interno della funzione anonima e quindi ho pensato che fosse possibile. Somiglia a questo:

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

Perché non posso farlo?

C'è qualche altro (buono) modo per farlo?

Informazioni bonus; questo deve funzionare solo in Safari, da qui il supporto IE mancante.


Perché non salvare questa funzione? Il gestore di eventi potrebbe non essere una funzione anonima.
kirilloid

2
Mi rendo conto che è un po 'tardi, ma puoi anche usare i metodi Node.setUserData /Node.getUserData per memorizzare i dati su un elemento. Ad esempio, quando è necessario impostare un listener anon (ed essere in grado di rimuoverlo), impostare prima userdata su una funzione anon (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);e quindi eseguire Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false); ... e lo stesso per removeEventListener. Spero che tu possa vederlo bene.
Chase

EDIT: come da commento precedente, immagino che funzioni solo su Firefox ... Ho appena provato IE8 (IE9 sconosciuto), Safari 5.1.2, Chrome (?), Opera 11 .. Nessun dado
Chase

Risposte:


77

Credo che questo sia il punto di una funzione anonima, manca un nome o un modo per referenziarla.

Se fossi in te, creerei semplicemente una funzione con nome o la inserirò in una variabile in modo da avere un riferimento ad essa.

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

Puoi quindi rimuoverlo da

window.document.removeEventListener("keydown", handler);   

3
Grazie per la risposta. Sono andato con: var handler; window.document.addEventListener ("keydown", handler = function (e) {Ma quello che non capisco è perché "this" non fa riferimento al listener di eventi. Il listener di eventi non dovrebbe essere un oggetto?
bitkid

1
La thisparola chiave può creare confusione. Un buon posto per leggerlo è quirksmode.org/js/this.html
Adam Heath

Grazie mille. Questo è stato molto utile.
bitkid

Sto provando a farlo per bloccare un annuncio davvero persistente su un sito web. So che questo è il punto delle funzioni anonime, ma ciò non significa che non mi piacerebbe sapere come farlo.
Wyatt8740

@bitkid: all'interno di una funzione gestore (supponendo che non sia una funzione freccia), si thisriferisce all'elemento a cui viene aggiunto l'ascoltatore, non all'evento stesso (che sarebbe il parametro e). Quindi this === e.currentTarget. leggi developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey

100

se ci si trova all'interno della funzione effettiva, è possibile utilizzare arguments.callee come riferimento alla funzione. come in:

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

EDIT: questo non funzionerà se stai lavorando in modalità rigorosa ( "use strict";)


2
Questo è utile poiché preserva i vantaggi delle funzioni anonime (non inquinando lo spazio dei nomi ecc.).
bompf

3
provato nell'applicazione WinJS, ho ricevuto l'errore successivo: "L'accesso alla proprietà 'callee' di un oggetto arguments non è consentito in modalità rigorosa"
Valentin Kantor

1
@ValentinKantor: Questo perché in qualcosa nel codice c'è un "use strict"; dichiarazione, e non puoi usare chiamato in modalità rigorosa.
OMA

19
Assegna un nome alla funzione inline e puoi farvi riferimento senza ricorrere a arguments.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Love,

4
Come affermato in Mozilla: "Attenzione: la 5a edizione di ECMAScript (ES5) proibisce l'uso di arguments.callee () in modalità rigorosa. Evita di usare arguments.callee () dando un nome alle espressioni di funzione o usa una dichiarazione di funzione in cui una funzione deve chiamare se stesso. "
tizio

50

Una versione della soluzione di Otto Nascarella che funziona in modalità rigorosa è:

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

4
Bella soluzione!
Eric Norcross,

2
Questo potrebbe non essere il modo giusto, ma questa è la cosa più semplice.
Vignesh

7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

Possono essere diverse funzioni anonime, tasto 1

Attenzione: funziona solo in Chrome Dev Toolse non può essere utilizzato in code : link


2
Grazie, hai risolto un indovinello, almeno in Chrome, perché quello che molti jolly dicevano era impossibile. Amico, sei come ... Batman!
JasonXA

20
getEventListenerssembra far parte degli strumenti di sviluppo di Chrome, quindi non è realmente utilizzabile per qualcosa di diverso dal debug.
DBS

1
L'ho appena provato, confermato che è disponibile solo nei Devtools e non quando è incluso in uno script all'interno di una pagina.
Andres Riofrio

5

nei browser moderni puoi fare quanto segue ...

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

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


Fantastico finché non scopri che nessuna versione di IE né edge <16 supportano effettivamente questa funzione. Almeno tra 5 anni possiamo usarlo da allora IE sarà (leggi: dovrebbe) essere deprecato, Edge prenderà il suo posto e utilizzerà il motore webkit invece della loro cosa "EdgeHTML".
SidOfc

1
con questo polyfill per voci DOM Livello 4 dovresti stare bene npmjs.com/package/dom4
shunryu111

2

Un'opzione non così anonima

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

Da quando ho ricevuto feedback da Andy ( è giusto, ma come per molti esempi, ho voluto mostrare un'espansione contestuale dell'idea ), ecco un'esposizione meno complicata :

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

Ciò consente una struttura della funzione effettivamente anonima, evita l'uso del chiamato praticamente deprecato e consente una facile rimozione.

Per inciso : la rimozione dell'elemento script subito dopo aver impostato l'ascoltatore è un simpatico trucco per nascondere il codice che si preferirebbe non fosse del tutto ovvio ad occhi indiscreti ( rovinerebbe la sorpresa ;-)

Quindi il metodo ( più semplicemente ) è:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );

2
Questo è troppo complicato.
Ben Sinclair

@ Andy Sono d'accordo, più o meno, ma stavo cercando di dimostrare che semplicemente non c'è modo di rimuovere una funzione anonima. Si deve in qualche modo essere riferimento (anche chiamato (è cattivo, m'kay) fa riferimento la funzione), e quindi fornito un esempio di un (altro) modo la funzione può essere referenziati - e, come è costruito di parti che può ugualmente essere memorizzato per riferimento successivo (la parte importante). Ovviamente una funzione veramente anonima è in qualche modo costruita al volo, quindi è necessario conoscere anche in seguito quale azione / tipo di evento e se l'acquisizione è stata utilizzata. Comunque, ecco un metodo migliore :-)
Fred Gandt

Ha funzionato perfettamente per me. Non riuscivo a vedere un altro modo per passare argomenti nella funzione, poiché non poteva essere anonimo.
nicodemus13

2

Questo non è l'ideale in quanto rimuove tutto, ma potrebbe funzionare per le tue esigenze:

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

La clonazione di un nodo copia tutti i suoi attributi e i relativi valori, inclusi i listener intrinseci (in linea). Non copia i listener di eventi aggiunti utilizzando addEventListener ()

Node.cloneNode ()


Questo è assolutamente geniale
Ahmad Alfy

1

JavaScript : il metodo addEventListener registra il listener specificato sull'EventTarget (Elemento | documento | Finestra) su cui è chiamato.

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

Mouse, eventi della tastiera Esempio di 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);

Il metodo removeEventListener rimuove il listener di eventi precedentemente registrato con EventTarget.addEventListener ().

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

posso usare


1

Per dare un approccio più aggiornato a questo:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);

2
Una spiegazione sarebbe carina.
Poul Bak

1

Mi sono imbattuto nello stesso problema e questa è stata la migliore soluzione che ho potuto ottenere:

/*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;

Tieni presente che l'ho testato solo per l' windowelemento e per l' 'mousemove'evento, quindi potrebbero esserci dei problemi con questo approccio.


0

Forse non è la soluzione migliore in termini di ciò che stai chiedendo. Non ho ancora determinato un metodo efficiente per rimuovere la funzione anonima dichiarata in linea con l'invocazione del listener di eventi.

Personalmente uso una variabile per memorizzare <target>e dichiarare la funzione al di fuori dell'invocazione del listener di eventi, ad esempio:

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Quindi per rimuovere l'ascoltatore:

target.removeEventListener('click', myFunc);

Non è il miglior consiglio che riceverai, ma per rimuovere le funzioni anonime l'unica soluzione che ho trovato utile è rimuovere e sostituire l'elemento HTML. Sono sicuro che deve esserci un metodo JS vaniglia migliore ma non l'ho ancora visto.


0

So che questo è un thread abbastanza vecchio, ma ho pensato di mettere i miei due centesimi per coloro che lo trovano utile.

Lo script (scuse per i nomi dei metodi non creativi):

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

E puoi usarlo in questo modo:

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

-4
window.document.onkeydown = function(){};

Perché no = indefinito? Davvero tosto.
Ben Sinclair

2
Ciò non rimuoverà alcun gestore con cui è stato registrato addEventListener.
Luc125
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.