Javascript removeEventListener funktioniert nicht


78

Ich habe den folgenden Code, um eventListener hinzuzufügen

 area.addEventListener('click',function(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          },true);

Es funktioniert korrekt wie erwartet. Später in einer anderen Funktion habe ich versucht, den Ereignis-Listener mit dem folgenden Code zu entfernen

 area.removeEventListener('click',function(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          },true);

Aber der gerade Listener wird nicht entfernt. Warum passiert das? Gibt es ein Problem mit meinem removeEventListener ()? Hinweis: In diesem Bereich befindet sich etwa document.getElementById ('myId').


Antworten:


125

Dies liegt daran, dass zwei anonyme Funktionen völlig unterschiedliche Funktionen sind. Ihr removeEventListenerArgument ist kein Verweis auf das zuvor angehängte Funktionsobjekt.

function foo(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          }
 area.addEventListener('click',foo,true);
 area.removeEventListener('click',foo,true);

1
Ich danke dir sehr!
Woppi

54
+1 Richtig. bind(this)wird die Signatur ändern. Weisen Sie die Funktion daher immer einer varAfter-Bindung thiszur Verwendung der Funktions- bindAPI zu, damit diese in varverwendet werden kann removeListener. Sie werden dieses Problem in Typoskript
Nirus

4
Das wird nicht zulassen , dass Sie Funktionsparameter fe passierenfoo(1)
Herrgott

2
danke für ist kein Verweis auf das Funktionsobjekt, das zuvor angehängt wurde
Evhz

16
Wenn jemand Klassen benutzt, versuchen Sie etwas wie this.onClick = this.onClick.bind(this)vor jedem Zuhörer, dann btn.addEventListener('click', this.onClick)endlichbtn.removeEventListener('click', this.onClick)
Joseluisq

11

Ich finde, dass für das Windows-Objekt der letzte Parameter "true" erforderlich ist. Das Entfernen funktioniert nicht, wenn kein Erfassungsflag vorhanden ist.


Mein Problem wurde behoben. Vielen Dank.
James Manes

7

Sie erstellen in beiden Aufrufen zwei verschiedene Funktionen. Die zweite Funktion bezieht sich also in keiner Weise auf die erste, und der Motor kann die Funktion entfernen. Verwenden Sie stattdessen eine gemeinsame Kennung für die Funktion.

var handler = function(event) {
              app.addSpot(event.clientX,event.clientY);
              app.addFlag = 1;
          };
area.addEventListener('click', handler,true);

später können Sie den Handler dann durch Aufrufen entfernen

area.removeEventListener('click', handler,true);

1

Um es zu entfernen, speichern Sie die Funktion in einer Variablen oder verwenden Sie einfach eine benannte Funktion und übergeben Sie diese Funktion an den removeEventListenerAufruf:

function areaClicked(event) {
    app.addSpot(event.clientX, event.clientY);
    app.addFlag = 1;
}

area.addEventListener('click', areaClicked, true);
// ...
area.removeEventListener('click', areaClicked, true);

aber wie kann ich Argumente (hier Ereignis) an diese Funktion übergeben. Deshalb habe ich anonyme Funktion verwendet
Jinu Joseph Daniel

Es wird vom Browser übergeben. Es spielt keine Rolle, ob Sie die Funktion separat definieren oder nicht.
ThiefMaster

WARNUNG: Ich habe herausgefunden, was an meinem Ansatz falsch war. Die Methode removeEventListener () funktioniert NUR mit NAMED FUNCTIONS. Es funktioniert NICHT mit anonymen Funktionen! Als ich den Code bearbeitet habe, um dies zu berücksichtigen, hat alles wie geplant funktioniert. Sie müssen eine NAMED-Funktion in Ihrem Abschluss definieren und einen Verweis auf eine Instanz davon mit den vom Abschluss übergebenen Parametern zurückgeben. Tun Sie dies, und removeEventListener () funktioniert einwandfrei.
David Edwards

1

Wenn Sie lokale Variablen an die vom Ereignis-Listener aufgerufene Funktion übergeben möchten, können Sie die Funktion innerhalb der Funktion definieren (um die lokalen Variablen abzurufen) und den Namen der Funktion in der Funktion selbst übergeben. Beginnen wir beispielsweise mit der Funktion, die den Ereignis-Listener mit der App als lokaler Variable hinzufügt. Sie würden eine Funktion innerhalb dieser Funktion schreiben, wie z.

function yourFunction () {
    var app;

    function waitListen () {
        waitExecute(app, waitListen);
    }

    area.addEventListener('click', waitListen, true);
}

Dann haben Sie das, was Sie brauchen, um es zu entfernen, wenn waitExecute aufgerufen wird.

function waitExecute (app, waitListen) {
    ... // other code
    area.removeEventListener('click', waitListen, true);
}

Ich habe hier ein Problem festgestellt. Selbst wenn Sie eine Ereignishandlerfunktion definieren, einen Verweis auf diese Funktion speichern und diesen Verweis später an removeEventListener () übergeben, wird die Funktion nicht entfernt. Der Kommentar ist zu klein, um einen Code einzugeben. Wenn Sie also Code möchten, muss ich ein Antwortfeld verwenden ...
David Edwards

Nachtrag zu dem oben Gesagten: Ein weiteres interessantes Phänomen, das ich gefunden habe, ist, dass selbst wenn Sie angeben, dass Ihr Ereignis-Listener passiv ist, der alte immer noch in der Kette bleibt. Schlimmer noch, der alte wird jetzt zu einem blockierenden Ereignishandler, während der neue seinen passiven Status behält. Ich denke, hier ist eine Erklärung erforderlich.
David Edwards

0

Definieren Sie zuerst Ihren Event Handler.

und dann

area.addEventListener('click',handler);
area.removeEventListener('click',handler);

0

Ich habe ein Problem mit removeEventListener () festgestellt, das erklärt werden muss.

Ich wollte in der Lage sein, Parameter an Ereignis-Listener zu übergeben, also schrieb ich eine Funktion zum Generieren des Ereignis-Listeners, die wiederum eine zweite Funktion zurückgibt, die meinen beabsichtigten Ereignis-Listener als Rückruf aufruft.

Die vollständige Bibliotheksdatei lautet wie folgt:

//Event handler constants

function EventHandlerConstants()
{
this.SUCCESS = 0;   //Signals success of an event handler function
this.NOTFUNCTION = 1;   //actualHandler argument passed to MakeEventHandler() is not a Function object

//End constructor
}

//MakeEventHandler()

//Arguments:

//actualHandler : reference to the actual function to be called as the true event handler

//selfObject    : reference to whatever object is intended to be referenced via the "this" keyword within
//          the true event handler. Set to NULL if no such object is needed by your true
//          event handler specified in the actualHandler argument above.

//args      : array containing the arguments to be passed to the true event handler, so that the true
//          event handler can be written with named arguments, such as:

//          myEventHandler(event, arg1, arg2, ... )

//          If your function doesn't need any arguments, pass an empty array, namely [], as the
//          value of this argument.

//Usage:

//c = new EventHandlerConstants();
//res = MakeEventHandler(actualHandler, selfObject, args);
//if (res == c.SUCCESS)
//  element.addEventListener(eventType, res.actualHandler, true);   //or whatever


function MakeEventHandler(actualHandler, selfObject, args)
{
var c = new EventHandlerConstants();

var funcReturn = null;      //This will contain a reference to the actual function generated and passed back to
                //the caller

var res = {
        "status" : c.SUCCESS,
        "actualHandler" : null
        };

if (IsGenuineObject(actualHandler, Function))
{
    res.actualHandler = function(event) {

        var trueArgs = [event].concat(args);

        actualHandler.apply(selfObject, trueArgs);

    };

}
else
{
    res.status = c.NOTFUNCTION;

//End if/else
}

//Return our result object with appropriate properties set ...

return(res);

//End function
}

Dann schrieb ich eine kurze Testseite, um herauszufinden, ob dies wie beabsichtigt funktionierte, und erlaubte mir, Ereignishandler nach Belieben hinzuzufügen UND zu entfernen.

Die HTML-Testseite lautet wie folgt:

<!DOCTYPE html>
<html>
<head>

<!-- CSS goes here -->

<link rel="stylesheet" type="text/css" href="NewEventTest.css">

<!-- Required JavaScript library files -->

<script language = "JavaScript" src="BasicSupport.js"></script>
<script language = "JavaScript" src="EventHandler6.js"></script>

</head>

<body class="StdC" id="MainApplication">

<button type="button" class="StdC NoSwipe" id="Button1">Try Me Out</button>

<button type="button" class="StdC NoSwipe" id="Button2">Alter The 1st Button</button>

</body>

<script language = "JavaScript" src="NewEventTest.js"></script>

</html>

Der Vollständigkeit halber verwende ich auch die folgende einfache CSS-Datei:

/* NewEventTest.css */


/* Define standard display settings classes for a range of HTML elements */

.StdC {

color: rgba(255, 255, 255, 1);
background-color: rgba(0, 128, 0, 1);
font-family: "Book Antiqua", "Times New Roman", "Times", serif;
font-size: 100%;
font-weight: normal;
text-align: center;

}


.NoSwipe {

user-select: none;  /* Stops text from being selectable! */

}

Der Testcode lautet wie folgt:

//NewEventTest.js


function GlobalVariables()
{
this.TmpRef1 = null;
this.TmpRef2 = null;
this.TmpRef3 = null;

this.Const1 = null;

this.Handler1 = null;
this.Handler2 = null;
this.Handler3 = null;

this.EventOptions = {"passive" : true, "capture" : true };

//End constructor
}


//Button 1 Initial function

function Button1Initial(event)
{
console.log("Button 1 initial event handler triggered");

//End event handler
}


function Button1Final(event)
{
console.log("Button 1 final event handler triggered");

//End event handler
}


function Button2Handler(event, oldFunc, newFunc)
{
var funcRef = null;

this.removeEventListener("click", oldFunc);
this.addEventListener("click", newFunc, GLOBALS.EventOptions);

//End event handler
}


//Application Setup

GLOBALS = new GlobalVariables();

GLOBALS.Const1 = new EventHandlerConstants();

GLOBALS.TmpRef1 = document.getElementById("Button1");
GLOBALS.TmpRef2 = MakeEventHandler(Button1Initial, null, []);
if (GLOBALS.TmpRef2.status == GLOBALS.Const1.SUCCESS)
{
    GLOBALS.Handler1 = GLOBALS.TmpRef2.actualHandler;
    GLOBALS.TmpRef1.addEventListener("click", GLOBALS.Handler1, GLOBALS.EventOptions);

//End if
}

GLOBALS.TmpRef1 = MakeEventHandler(Button1Final, null, []);
if (GLOBALS.TmpRef1.status == GLOBALS.Const1.SUCCESS)
{
    GLOBALS.Handler3 = GLOBALS.TmpRef1.actualHandler;

//End if
}


GLOBALS.TmpRef1 = document.getElementById("Button2");
GLOBALS.TmpRef2 = document.getElementById("Button1");
GLOBALS.TmpRef3 = Button1Final;
GLOBALS.TmpRef4 = MakeEventHandler(Button2Handler, GLOBALS.TmpRef2, [GLOBALS.Handler1, GLOBALS.Handler3]);
if (GLOBALS.TmpRef4.status == GLOBALS.Const1.SUCCESS)
{
    GLOBALS.Handler2 = GLOBALS.TmpRef4.actualHandler;
    GLOBALS.TmpRef1.addEventListener("click", GLOBALS.Handler2, GLOBALS.EventOptions);

//End if
}

Der durchzuführende Test ist also wie folgt:

[1] Fügen Sie einen Klickereignishandler zu Schaltfläche 1 hinzu.

[2] Testen Sie, ob der Ereignishandler aufgerufen wird, wenn ich auf die Schaltfläche klicke.

[3] Wenn dieser Test bestanden ist, klicken Sie auf Schaltfläche 2 und rufen Sie den daran angehängten Ereignishandler auf, der den alten Ereignishandler entfernt, der an Schaltfläche 1 angehängt ist, und ihn dann durch einen neuen Ereignishandler ersetzt.

Die Schritte [1] und [2] funktionieren einwandfrei. Der Ereignishandler wird angehängt und aufgerufen, wenn ich auf die Schaltfläche klicke.

Das Problem liegt bei Schritt [3].

Obwohl ich einen Verweis auf die von MakeEventHandler () generierte Funktion speichere, insbesondere um diesen Ereignis-Listener in Schritt [3] zu entfernen, entfernt der Aufruf von removeEventListener () den Ereignis-Listener NICHT. Das anschließende Klicken auf Button # 1 löst BEIDE Ereignis-Listener aus, einschließlich desjenigen, den ich angeblich entfernt habe!

Unnötig zu erwähnen, dass dieses Verhalten ich rätselhaft finde, obwohl ich alles sorgfältig so eingerichtet habe, dass die Funktion, die ich im Aufruf von removeEventListener () angegeben habe, dieselbe Funktion ist, die ich ursprünglich mit addEventListener () hinzugefügt habe - gemäß der gesamten Dokumentation zu diesem Thema I. Ich habe gelesen (einschließlich dieses Threads), dass die Übergabe eines Verweises auf dieselbe Funktion für jeden Aufruf funktionieren sollte , aber eindeutig nicht.

In Schritt [1] lautet die Testausgabe in der Konsole wie erwartet:

Die erste Ereignishandler-Taste 1 wurde ausgelöst

Der Code wird erwartungsgemäß auch in Schritt [2] ausgeführt, und eine schrittweise Verfolgung des Codes zeigt, dass der Code tatsächlich wie erwartet ausgeführt wird.

Aber in Schritt [3], während der erste Klick auf Button # 1 das gewünschte Ergebnis liefert:

Die letzte Ereignisbehandlungsroutine für Schaltfläche 1 wird ausgelöst

Was passiert, wenn anschließend auf Schaltfläche 1 geklickt wird, ist Folgendes :

Der erste Ereignishandler von Schaltfläche 1 wurde ausgelöst. Der letzte Ereignishandler von Schaltfläche 1 wurde ausgelöst

Selbst wenn die ursprünglich an Schaltfläche 1 angehängte Funktion im Speicher verbleibt, weil sie innerhalb eines Abschlusses generiert wurde, sollte sie dennoch von der Ereignis-Listener-Sammlung für das Element getrennt werden. Warum ist es noch verbunden?

Oder bin ich auf einen seltsamen Fehler gestoßen, bei dem Schließungen mit Ereignis-Listenern verwendet wurden, der gemeldet werden muss?


2
Sie sollten eine neue Frage stellen. Dieser Bereich beantwortet die Frage des OP.
VectorVortec

Ich bin auch darauf gestoßen. Das ist Unsinn. Musste auf schmutzige Methoden zurückgreifen, um durchzukommen, dh. Verfolgen Sie, welche Ereignisse im Fensterobjekt aktiviert / deaktiviert werden sollen.
Mave
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.