removeEventListener sur les fonctions anonymes en JavaScript

103

J'ai un objet qui contient des méthodes. Ces méthodes sont placées dans l'objet à l'intérieur d'une fonction anonyme. Cela ressemble à ceci:

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

(il y a beaucoup plus de code, mais cela suffit pour montrer le problème)

Maintenant, je veux arrêter l'écouteur d'événements dans certains cas. Par conséquent, j'essaie de faire un removeEventListener mais je ne peux pas comprendre comment faire cela. J'ai lu dans d'autres questions qu'il n'est pas possible d'appeler removeEventListener sur des fonctions anonymes, mais est-ce également le cas dans cette situation?

J'ai créé une méthode dans la fonction anonyme et j'ai donc pensé que c'était possible. Ressemble à ça:

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

Pourquoi je ne peux pas faire ça?

Y a-t-il une autre (bonne) façon de faire cela?

Info bonus; cela ne doit fonctionner que dans Safari, d'où le support IE manquant.

bitkid
la source
Pourquoi ne pas enregistrer cette fonction? Le gestionnaire d'événements n'est peut-être pas une fonction anonyme.
kirilloid
2
Je me rends compte que c'est un peu tard, mais vous pouvez également utiliser les méthodes Node.setUserData /Node.getUserData pour stocker des données sur un élément. Par exemple, lorsque vous avez besoin de définir un écouteur anon (et de pouvoir le supprimer), définissez d'abord userdata sur une fonction anon (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);, puis faites Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false); ... et de même pour removeEventListener. J'espère que vous pouvez voir cela bien.
Poursuite du
EDIT: Selon le commentaire précédent, je suppose que cela ne fonctionne que dans Firefox ... Je viens d'essayer IE8 (IE9 inconnu), Safari 5.1.2, Chrome (?), Opera 11..Pas de dés
Chase
Possible duplication de JavaScript: supprimer l'écouteur d'événement
Heretic Monkey

Réponses:

78

Je crois que c'est le but d'une fonction anonyme, il lui manque un nom ou un moyen de la référencer.

Si j'étais vous, je créerais simplement une fonction nommée, ou je la mettrais dans une variable pour que vous y fassiez référence.

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

Vous pouvez ensuite le supprimer en

window.document.removeEventListener("keydown", handler);   
Adam Heath
la source
3
Merci pour votre réponse. Je suis allé avec: var handler; window.document.addEventListener ("keydown", handler = function (e) {Mais ce que je ne comprends pas, c'est pourquoi "this" ne fait pas référence à l'écouteur d'événements. L'écouteur d'événements ne devrait-il pas être un objet?
bitkid
1
Le thismot clé peut prêter à confusion. Un bon endroit pour en savoir plus est quirksmode.org/js/this.html
Adam Heath
Merci beaucoup. Cela a été très utile.
bitkid
J'essaie de faire cela pour bloquer une annonce vraiment persistante sur un site Web. Je sais que c'est le but des fonctions anonymes, mais cela ne veut pas dire que je n'aimerais pas savoir comment faire.
Wyatt8740
@bitkid: Dans une fonction de gestionnaire (en supposant qu'il ne s'agisse pas d'une fonction de flèche), le thisfait référence à l'élément auquel l'écouteur est ajouté, pas à l'événement lui-même (qui serait le paramètre e). Par conséquent this === e.currentTarget. lire developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey
100

si vous êtes dans la fonction réelle, vous pouvez utiliser arguments.callee comme référence à la fonction. un péché:

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

EDIT: Cela ne fonctionnera pas si vous travaillez en mode strict ( "use strict";)

Otto Nascarella
la source
2
C'est bien car cela préserve les avantages des fonctions anonymes (ne polluant pas l'espace de noms etc.).
bompf
3
essayé cela dans l'application WinJS, j'ai obtenu l'erreur suivante: "L'accès à la propriété 'callee' d'un objet arguments n'est pas autorisé en mode strict"
Valentin Kantor
1
@ValentinKantor: Parce que quelque chose dans le code il y a un "use strict"; et vous ne pouvez pas utiliser l'appelé en mode strict.
OMA
19
Donnez un nom à la fonction en ligne et vous pourrez la référencer sans avoir recours à des arguments.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Love
4
Comme indiqué dans Mozilla: "Attention: La 5ème édition d'ECMAScript (ES5) interdit l'utilisation d'arguments.callee () en mode strict. Évitez d'utiliser arguments.callee () en donnant un nom aux expressions de fonction ou en utilisant une déclaration de fonction où une fonction doit s'appeler. "
mec
50

Une version de la solution d' Otto Nascarella qui fonctionne en mode strict est:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});
Melle
la source
4
Belle solution!
Eric Norcross
2
Ce n'est peut-être pas la bonne façon, mais c'est la chose la plus simple.
Vignesh
7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

Peut être plusieurs fonctions anonymes, keydown 1

Attention: ne fonctionne que dans Chrome Dev Tools& ne peut pas être utilisé dans code : link

Isayevskiy_Sergey
la source
2
Merci, vous avez résolu une énigme, au moins dans Chrome, car ce que de nombreux jokers ont dit était impossible. Mec, tu es comme ... Batman!
JasonXA
20
getEventListenerssemble faire partie des outils de développement Chrome, il n'est donc pas vraiment utilisable pour autre chose que le débogage.
DBS
1
Je viens de l'essayer, j'ai confirmé qu'il n'est disponible que dans les outils de développement et non lorsqu'il est inclus dans un script à l'intérieur d'une page.
Andres Riofrio
5

dans les navigateurs modernes, vous pouvez effectuer les opérations suivantes ...

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

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

shunryu111
la source
Cool jusqu'à ce que vous découvriez qu'aucune version d'IE ni edge <16 ne supporte réellement cette fonctionnalité. Au moins dans 5 ans, nous pourrons l'utiliser puisque IE sera (lisez: devrait) être obsolète, Edge aura pris sa place et il utilisera le moteur Webkit au lieu de leur propre truc "EdgeHTML".
SidOfc
1
avec ce polyfill pour les entrées DOM niveau 4, vous devriez être bien npmjs.com/package/dom4
shunryu111
2

Une option pas si anonyme

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

Depuis que j'ai reçu des commentaires d'Andy ( tout à fait raison, mais comme pour de nombreux exemples, je souhaitais montrer une expansion contextuelle de l'idée ), voici une exposition moins compliquée :

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

Cela permet une structure de fonction effectivement anonyme, évite l'utilisation de l' appelé pratiquement obsolète et permet une suppression facile.

Incidemment : la suppression de l'élément de script immédiatement après avoir défini l'auditeur est une astuce mignonne pour cacher du code que l'on préférerait ne pas être tout à fait évident pour les regards indiscrets ( cela gâcherait la surprise ;-)

Donc la méthode ( plus simplement ) est:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );
Fred Gandt
la source
2
C'est trop compliqué.
Ben Sinclair le
@Andy Je suis d'accord, en quelque sorte, mais j'essayais de montrer qu'il n'y a tout simplement aucun moyen de supprimer une fonction anonyme. Il doit d'une certaine manière être référencé (même l'appelé (est mauvais, M'Kay) fait référence à la fonction), et donc fourni un exemple d'une seule (autre) façon dont la fonction peut être référencée - et, comment elle est construite de parties qui peut également être stocké pour référence ultérieure (la partie importante). De toute évidence, une fonction véritablement anonyme est quelque peu construite à la volée, et donc savoir plus tard quelle action / type d'événement et si la capture a été utilisée doit également être connu. Quoi qu'il en soit, voici une meilleure méthode :-)
Fred Gandt
A parfaitement fonctionné pour moi. Je ne voyais pas d'autre moyen de passer des arguments dans la fonction, car elle ne pouvait pas être anonyme.
nicodemus13
2

Ce n'est pas idéal car cela supprime tout, mais pourrait fonctionner pour vos besoins:

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

Le clonage d'un nœud copie tous ses attributs et leurs valeurs, y compris les écouteurs intrinsèques (en ligne). Il ne copie pas les écouteurs d'événements ajoutés à l'aide de addEventListener ()

Node.cloneNode ()

Communauté
la source
C'est absolument génial
Ahmad Alfy
1

JavaScript : la méthode addEventListener enregistre l'écouteur spécifié sur l'objet EventTarget (élément | document | fenêtre) sur lequel il est appelé.

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

Evénements souris, clavier Exemple de test dans 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 supprime l'écouteur d'événements précédemment enregistré avec EventTarget.addEventListener ().

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

puis-je utiliser

Yash
la source
1

Pour donner une approche plus à jour à cela:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);
Jonatas Walker
la source
2
Une explication serait bien.
Poul Bak
1

Je suis tombé sur le même problème et c'était la meilleure solution que je pouvais obtenir:

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

Gardez à l'esprit que je n'ai testé cela que pour l' windowélément et pour l' 'mousemove'événement, donc il pourrait y avoir des problèmes avec cette approche.

Gark Garcia
la source
0

Peut-être pas la meilleure solution en termes de ce que vous demandez. Je n'ai toujours pas déterminé de méthode efficace pour supprimer la fonction anonyme déclarée en ligne avec l'invocation de l'écouteur d'événements.

J'utilise personnellement une variable pour stocker <target>et déclarer la fonction en dehors de l'appel de l'écouteur d'événements, par exemple:

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

Ensuite, pour supprimer l'auditeur:

target.removeEventListener('click', myFunc);

Ce n'est pas la principale recommandation que vous recevrez, mais pour supprimer les fonctions anonymes, la seule solution que j'ai trouvée utile est de supprimer puis de remplacer l'élément HTML. Je suis sûr qu'il doit y avoir une meilleure méthode JS vanille mais je ne l'ai pas encore vue.

Shay Connolly
la source
0

Je sais que c'est un fil assez ancien, mais j'ai pensé que je pourrais mettre mes deux cents pour ceux qui le trouvent utile.

Le script (excuses pour les noms de méthodes non créatives):

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

Et vous pouvez l'utiliser comme ceci:

// 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.
la source
-4
window.document.onkeydown = function(){};
Dun
la source
Pourquoi pas = indéfini? Vraiment dur à cuire.
Ben Sinclair, le
2
Cela ne supprimera aucun gestionnaire enregistré avec addEventListener.
Luc125 du