removeEventListener für anonyme Funktionen in JavaScript

101

Ich habe ein Objekt, das Methoden enthält. Diese Methoden werden innerhalb einer anonymen Funktion in das Objekt eingefügt. Es sieht aus wie das:

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

(Es gibt viel mehr Code, aber dies reicht aus, um das Problem aufzuzeigen.)

Jetzt möchte ich den Ereignis-Listener in einigen Fällen stoppen. Daher versuche ich, einen removeEventListener zu erstellen, kann aber nicht herausfinden, wie das geht. Ich habe in anderen Fragen gelesen, dass es nicht möglich ist, removeEventListener für anonyme Funktionen aufzurufen, aber ist dies auch in dieser Situation der Fall?

Ich habe eine Methode in t innerhalb der anonymen Funktion erstellt und dachte daher, dass dies möglich ist. Sieht aus wie das:

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

Warum kann ich das nicht tun?

Gibt es einen anderen (guten) Weg, dies zu tun?

Bonusinfo; Dies muss nur in Safari funktionieren, daher die fehlende IE-Unterstützung.

bitkid
quelle
Warum diese Funktion nicht speichern? Der Ereignishandler ist möglicherweise keine anonyme Funktion.
Kirilloid
2
Mir ist klar, dass dies etwas spät ist, aber Sie können auch die Methoden Node.setUserData /Node.getUserData verwenden, um Daten zu einem Element zu speichern. Wenn Sie beispielsweise einen anon-Listener festlegen müssen (und ihn entfernen können), setzen Sie zuerst Benutzerdaten auf eine anon-Funktion (Elem.setUserData('eventListener', function(e){console.log('Event fired.');}, null);und führen Sie dann Elem.addEventListener ('event', Elem.getUserData ('eventListener'), false) aus. ... und das Gleiche gilt für removeEventListener. Hoffe du kannst das in Ordnung sehen.
Chase
EDIT: Wie im vorherigen Kommentar erwähnt, funktioniert das wohl nur in Firefox ... Ich habe gerade IE8 (IE9 unbekannt), Safari 5.1.2, Chrome (?), Opera 11 ausprobiert. Keine Würfel
Chase
Mögliches Duplikat von JavaScript: Ereignis-Listener entfernen
Heretic Monkey

Antworten:

76

Ich glaube, das ist der Punkt einer anonymen Funktion, es fehlt ein Name oder eine Möglichkeit, darauf zu verweisen.

Wenn ich Sie wäre, würde ich einfach eine benannte Funktion erstellen oder sie in eine Variable einfügen, damit Sie einen Verweis darauf haben.

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

Sie können es dann durch entfernen

window.document.removeEventListener("keydown", handler);   
Adam Heath
quelle
3
Danke für Ihre Antwort. Ich ging mit: var handler; window.document.addEventListener ("keydown", handler = function (e) {Was ich aber nicht verstehe, ist, warum "this" nicht auf den Ereignis-Listener verweist. Sollte der Ereignis-Listener kein Objekt sein?
bitkid
1
Das thisSchlüsselwort kann verwirrend sein. Ein guter Ort, um sich darüber zu informieren,
Adam Heath
Vielen Dank. Das war sehr hilfreich.
Bitkid
Ich versuche dies zu tun, um eine wirklich dauerhafte Anzeige auf einer Website zu blockieren. Ich weiß, dass dies der Punkt anonymer Funktionen ist, aber das bedeutet nicht, dass ich nicht wissen möchte, wie das geht.
Wyatt8740
@bitkid: Innerhalb einer Handlerfunktion (vorausgesetzt, es handelt sich nicht um eine Pfeilfunktion) thisbezieht sich das auf das Element, zu dem der Listener hinzugefügt wird, nicht auf das Ereignis selbst (das der Parameter wäre e). Deshalb this === e.currentTarget. Lesen Sie developer.mozilla.org/en-US/docs/Web/API/EventTarget/…
chharvey
99

Wenn Sie sich innerhalb der eigentlichen Funktion befinden, können Sie argument.callee als Referenz auf die Funktion verwenden. wie in:

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

BEARBEITEN: Dies funktioniert nicht, wenn Sie im strengen Modus arbeiten ( "use strict";)

Otto Nascarella
quelle
1
Dies ist schön, da die Vorteile anonymer Funktionen erhalten bleiben (ohne den Namespace zu verschmutzen usw.).
Bompf
3
versuchte dies in der WinJS-App, bekam den nächsten Fehler: "Der Zugriff auf die 'callee'-Eigenschaft eines Argument-Objekts ist im strengen Modus nicht erlaubt"
Valentin Kantor
1
@ValentinKantor: Das, weil etwas im Code gibt es eine "use strict"; Anweisung, und Sie können Callee nicht im strengen Modus verwenden.
OMA
19
Geben Sie der Inline-Funktion einen Namen und Sie können darauf verweisen, ohne auf Argumente zurückzugreifen.callee:button.addEventListener('click', function handler() { this.removeEventListener('click', handler); });
Harry Love
4
Wie in Mozilla angegeben: "Warnung: Die 5. Ausgabe von ECMAScript (ES5) verbietet die Verwendung von Argumenten.callee () im strengen Modus. Vermeiden Sie die Verwendung von Argumenten.callee (), indem Sie Funktionsausdrücken entweder einen Namen geben oder eine Funktionsdeklaration verwenden, wenn eine Funktion muss sich selbst nennen. "
Alter
50

Eine Version von Otto Nascarellas Lösung, die im strengen Modus funktioniert, ist:

button.addEventListener('click', function handler() {
      ///this will execute only once
      alert('only once!');
      this.removeEventListener('click', handler);
});
Melle
quelle
4
Schöne Lösung!
Eric Norcross
2
Dies ist möglicherweise nicht der richtige Weg, aber dies ist die einfachste Sache.
Vignesh
7
window.document.removeEventListener("keydown", getEventListeners(window.document.keydown[0].listener));  

Kann mehrere anonyme Funktionen sein, Keydown 1

Warnung: Funktioniert nur in Chrome Dev Tools& kann nicht in Code : Link verwendet werden

Isayevskiy_Sergey
quelle
2
Vielen Dank, Sie haben zumindest in Chrome ein Rätsel gelöst, denn das, was viele Joker sagten, war unmöglich. Mann, du bist wie ... Batman!
JasonXA
20
getEventListenersscheint Teil der Chrome Dev-Tools zu sein, kann also nur für das Debuggen verwendet werden.
DBS
1
Ich habe es gerade ausprobiert und bestätigt, dass es nur in den Devtools verfügbar ist und nicht, wenn es in einem Skript auf einer Seite enthalten ist.
Andres Riofrio
5

In modernen Browsern können Sie Folgendes tun ...

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

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

shunryu111
quelle
Cool, bis Sie herausfinden, dass keine Version von IE oder Edge <16 diese Funktion tatsächlich unterstützt. Zumindest in 5 Jahren können wir dies verwenden, da dann der IE (lesen: sollte) veraltet sein wird, Edge seinen Platz eingenommen hat und die Webkit-Engine anstelle ihrer eigenen "EdgeHTML" -Ding verwendet.
SidOfc
1
Mit dieser Polyfüllung für DOM Level 4-Einträge sollte es Ihnen gut gehen. npmjs.com/package/dom4
shunryu111
2

Eine nicht so anonyme 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);

Seit ich Feedback von Andy erhalten habe ( ganz richtig, aber wie bei vielen Beispielen wollte ich eine kontextbezogene Erweiterung der Idee zeigen ), ist hier eine weniger komplizierte Darstellung:

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

Dies ermöglicht eine effektiv anonyme Funktionsstruktur, vermeidet die Verwendung des praktisch veralteten Angerufenen und ermöglicht ein einfaches Entfernen.

Übrigens : Das Entfernen des Skriptelements unmittelbar nach dem Einstellen des Listeners ist ein niedlicher Trick, um Code zu verbergen, den man bevorzugen würde, war für neugierige Blicke nicht ganz offensichtlich ( würde die Überraschung verderben ;-)

Die Methode ( einfacher ) lautet also:

element.addEventListener( action, function name () {
    doSomething();
    element.removeEventListener( action, name, capture );
}, capture );
Fred Gandt
quelle
2
Das ist zu kompliziert.
Ben Sinclair
@Andy Ich stimme zu, habe aber versucht zu zeigen, dass es einfach keine Möglichkeit gibt, eine anonyme Funktion zu entfernen. Es muss in irgendeiner Weise referenziert werden (sogar Angerufene (ist schlecht, M'Kay) verweist auf die Funktion) und somit ein Beispiel für nur eine (andere) Art und Weise, wie die Funktion referenziert werden kann - und wie sie aus Teilen aufgebaut ist, die kann ebenfalls zur späteren Bezugnahme gespeichert werden (der wichtige Teil). Offensichtlich ist eine wirklich anonyme Funktion im laufenden Betrieb aufgebaut, und daher muss auch bekannt sein, welche Ereignisaktion / welcher Ereignistyp und welche Erfassung verwendet wurde. Wie auch immer, hier ist eine bessere Methode :-)
Fred Gandt
Hat perfekt für mich funktioniert. Ich konnte keinen anderen Weg finden, um Argumente an die Funktion zu übergeben, da sie nicht anonym sein konnte.
nicodemus13
2

Dies ist nicht ideal, da es alle entfernt, aber möglicherweise für Ihre Anforderungen funktioniert:

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

Durch das Klonen eines Knotens werden alle seine Attribute und ihre Werte kopiert, einschließlich der intrinsischen (Inline-) Listener. Es werden keine Ereignis-Listener kopiert, die mit addEventListener () hinzugefügt wurden.

Node.cloneNode ()

Gemeinschaft
quelle
Das ist absolut genial
Ahmad Alfy
1

JavaScript : Die Methode addEventListener registriert den angegebenen Listener in dem EventTarget (Element | Dokument | Fenster), auf dem sie aufgerufen wird.

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

Maus, Tastatur Ereignisse Beispiel Tests 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- Methode entfernt den zuvor bei EventTarget.addEventListener () registrierten Ereignis-Listener.

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

kann ich benutzen

Yash
quelle
1

Um dies auf den neuesten Stand zu bringen:

//one-time fire
element.addEventListener('mousedown', {
  handleEvent: function (evt) {
    element.removeEventListener(evt.type, this, false);
  }
}, false);
Jonatas Walker
quelle
2
Eine Erklärung wäre schön.
Poul Bak
1

Ich bin auf dasselbe Problem gestoßen und dies war die beste Lösung, die ich bekommen konnte:

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

Bitte denken Sie daran, dass ich dies nur für das windowElement und für die 'mousemove'Veranstaltung getestet habe , daher kann es bei diesem Ansatz zu Problemen kommen.

Gark Garcia
quelle
0

Möglicherweise nicht die beste Lösung in Bezug auf das, was Sie fragen. Ich habe immer noch keine effiziente Methode zum Entfernen anonymer Funktionen festgelegt, die im Einklang mit dem Aufruf des Ereignis-Listeners deklariert wurden.

Ich persönlich verwende eine Variable, um die <target>Funktion außerhalb des Aufrufs des Ereignis-Listeners zu speichern und zu deklarieren, z.

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

function myFunc(event) { function code; }

target.addEventListener('click', myFunc);

So entfernen Sie den Listener:

target.removeEventListener('click', myFunc);

Nicht die beste Empfehlung, die Sie erhalten, aber um anonyme Funktionen zu entfernen, ist die einzige Lösung, die ich als nützlich empfunden habe, das HTML-Element zu entfernen und dann zu ersetzen. Ich bin sicher, dass es eine bessere Vanille-JS-Methode geben muss, aber ich habe sie noch nicht gesehen.

Shay Connolly
quelle
0

Ich weiß, dass dies ein ziemlich alter Thread ist, dachte aber, ich könnte meine zwei Cent für diejenigen einsetzen, die ihn nützlich finden.

Das Skript (Entschuldigung für die unkreativen Methodennamen):

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

Und Sie können es so verwenden:

// 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.
quelle
-4
window.document.onkeydown = function(){};
Dun
quelle
Warum nicht = undefiniert? Echter Arsch.
Ben Sinclair
2
Dadurch wird kein Handler entfernt, bei dem registriert wurde addEventListener.
Luc125