que he encontrado un problema con removeEventListener() que necesita ser explicado.
Quería poder pasar parámetros a los detectores de eventos, así que escribí una función para generar el detector de eventos, que a su vez devuelve una segunda función, que llama a mi receptor de eventos previsto como una devolución de llamada.
El archivo de biblioteca completa es la siguiente:
//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
}
Entonces me escribió una página de prueba rápida para averiguar si esto funcionó como se esperaba, y me ha permitido añadir y eliminar controladores de eventos a voluntad.
página de prueba
El HTML es el siguiente:
<!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>
Para completar, utilizo el siguiente simple de archivos CSS, así:
/* 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! */
}
El código de prueba es el siguiente:
//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
}
Por lo tanto, la prueba a realizar es el siguiente:
[1] Adjunte un controlador de evento click al Botón # 1;
[2] Prueba para ver si el controlador de eventos se invoca cuando hago clic en el botón;
[3] Una vez que se aprueba la prueba, haga clic en el botón n. ° 2 e invoque el controlador de eventos adjunto, que elimina el antiguo controlador de eventos adjunto al botón n. ° 1 y lo reemplaza con un nuevo controlador de eventos.
Los pasos [1] y [2] funcionan bien. El controlador de eventos se adjunta e invoca cada vez que hago clic en el botón.
El problema es con el Paso [3].
Aunque guardo una referencia a la función generada por MakeEventHandler(), específicamente con el propósito de eliminar ese detector de eventos en el Paso [3], la llamada a removeEventListener() NO elimina el detector de eventos. Al hacer clic en el botón n. ° 1, se apagan AMBOS detectores de eventos, incluido el que supuestamente eliminé.
hace falta decir que este comportamiento me parece desconcertante, a pesar de marcar todo cuidadosamente de modo que la función que especifica en la llamada a removeEventListener() es la misma función de auto añadí inicialmente con addEventListener() - de acuerdo con todo la documentación sobre el tema que he leído (incluido este hilo), pasando una referencia a la misma función para cada llamada debería funcionar, pero claramente no.
En el Paso [1], la salida de prueba en la consola lee, como se esperaba:
Botón 1 controlador de eventos inicial desencadena
El código también es correr, como era de esperar, en el paso [2], y un rastro paso a paso del código revela que, de hecho, el código se ejecuta como se esperaba.
pero en el paso [3], mientras que el primera clic en el botón # 1 produce el resultado deseado:
Botón 1 manejador de evento final provocó
lo que sucede cuando el botón # 1 se hace clic en posteriormente es esto:
Botón 1 gestor de eventos inicial tr iggered Botón 1 manejador de evento final provocó
Sin duda, incluso si la función fijada inicialmente para el botón # 1 todavía persiste en la memoria, ya que se genera dentro de un cierre, que aún debe ser separado de la colección de detector de eventos para ¿el elemento? ¿Por qué todavía está conectado?
¿O me he encontrado alguna extraña falla que implica el uso de cierres con detectores de eventos, eso necesita ser informado?
¡Muchas gracias! – Woppi
+1 Es cierto. 'bind (this)' cambiará la firma. Por lo tanto, siempre asigne la función a 'var' después de vincular' this' a la función 'bind' API para que se pueda usar la misma' var' en 'removeListener'. Verá este problema más evidente en el mecanografiado – Nirus
que no le permitirá pasar los parámetros de la función, es decir, 'foo (1)' – Herrgott