Ho riscontrato un problema con removeEventListener() che deve essere spiegato.
Volevo essere in grado di passare parametri agli ascoltatori di eventi, così ho scritto una funzione per generare il listener di eventi, che a sua volta restituisce una seconda funzione, che chiama il listener di eventi previsto come callback.
Il file di libreria completa è la seguente:
//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
}
Poi ho scritto una pagina di prova rapida per scoprire se questo ha funzionato come previsto, e mi ha permesso di aggiungere e rimuovere i gestori di eventi a volontà.
pagina di prova
L'HTML è il seguente:
<!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>
Per completezza, io uso il seguente file semplice CSS così:
/* 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! */
}
Il codice di prova è la seguente:
//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
}
Quindi, il test da eseguire è il seguente:
[1] Allegare un gestore di eventi click al pulsante n. 1;
[2] Verificare se il gestore eventi viene richiamato quando faccio clic sul pulsante;
[3] Una volta superato il test, fare clic sul pulsante n.
I passaggi [1] e [2] funzionano correttamente. Il gestore di eventi è allegato e richiamato ogni volta che clicco sul pulsante.
Il problema riguarda Step [3].
Anche se si salva un riferimento alla funzione generata da MakeEventHandler(), in particolare allo scopo di rimuovere quel listener di eventi nel passaggio [3], la chiamata a removeEventListener() NON rimuove il listener di eventi. Il clic successivo sul pulsante 1 attiva tutti gli ascoltatori di eventi, incluso quello che presumibilmente ho rimosso!
Inutile dire che questo comportamento trovo sconcertante, nonostante l'impostazione con attenzione tutto ciò in modo che la funzione I specificare nella chiamata a removeEventListener() è la stessa funzione di auto ho aggiunto inizialmente con addEventListener() - secondo tutto la documentazione sull'argomento che ho letto (compreso questo thread), passando un riferimento alla stessa funzione per ogni chiamata dovrebbe funzionare, ma chiaramente non lo fa.
Al Passo [1], l'uscita di test nella console legge, come previsto:
Pulsante 1 gestore di eventi iniziale innescato
Il codice è anche funzionare, come previsto, nel passaggio [2], e una traccia passo passo del codice rivela che, in effetti, il codice viene eseguito come previsto.
Ma al punto [3], mentre il prima Fare clic sul pulsante # 1 si ottiene il risultato desiderato:
Pulsante 1 gestore dell'evento finale innescato
cosa succede quando il pulsante 1 si fa clic su successivamente è questo:
Pulsante 1 iniziale gestore di eventi tr iggered Pulsante 1 gestore dell'evento finale innescato
Sicuramente, anche se la funzione inizialmente collegato al pulsante # 1 persiste ancora in memoria, perché è stato generato all'interno di una chiusura, dovrebbe ancora essere staccato dalla raccolta listener di eventi per l'elemento? Perché è ancora connesso?
Oppure ho riscontrato qualche strano bug riguardante l'utilizzo di chiusure con listener di eventi, che deve essere segnalato?
Grazie mille! – Woppi
+1 vero. 'bind (this)' cambierà la firma. Quindi assegna sempre la funzione a 'var' dopo aver vincolato' this' all'utilizzo della funzione 'bind' API in modo che lo stesso' var' possa essere usato in 'removeListener'. Vedrai questo problema più evidente nel dattiloscritto – Nirus
che non ti permetterà di passare parametri di funzione per esempio. 'pippo (1)' – Herrgott