Controlador de eventos Javascript con parámetros

89

Quiero hacer un eventHandler que pase el evento y algunos parámetros. El problema es que la función no obtiene el elemento. Aquí hay un ejemplo:

doClick = function(func){
    var elem = .. // the element where it is all about
    elem.onclick = function(e){
        func(e, elem);
    }
}
doClick(function(e, element){
    // do stuff with element and the event
});

El 'elem' debe definirse fuera de la función anónima. ¿Cómo puedo usar el elemento pasado dentro de la función anónima? ¿Hay alguna forma de hacer esto?

¿Y qué pasa con addEventListener? Parece que no puedo pasar el evento a través de un addEventListener en absoluto, ¿verdad?

Actualizar

Parecía solucionar el problema con 'esto'

doClick = function(func){
    var that = this;
    this.element.onclick = function(e){
        func(e, that);
    }
}

Donde esto contiene este elemento al que puedo acceder en la función.

El addEventListener

Pero me pregunto sobre addEventListener:

function doClick(elem, func){
    element.addEventListener('click', func(event, elem), false);
}
sebas2day
fuente
2
Tienes elem, elementos, elemento involucrado en tu pregunta. ¿Puedes ser menos confuso?
mihai
Lo siento, estaba tratando de dar un ejemplo de mi situación pero no salió bien, actualicé el código.
sebas2day
¿Estarías buscando e.target/ e.currentTarget?
Rolf
1
cómo enviar un parámetro si estoy usando esto: ok.addEventListener ("click", this.changeText.bind (this));
Alberto Acuña

Respuestas:

103

No entiendo exactamente qué está tratando de hacer su código, pero puede hacer que las variables estén disponibles en cualquier controlador de eventos utilizando las ventajas de los cierres de funciones:

function addClickHandler(elem, arg1, arg2) {
    elem.addEventListener('click', function(e) {
        // in the event handler function here, you can directly refer
        // to arg1 and arg2 from the parent function arguments
    }, false);
}

Dependiendo de su situación exacta de codificación, casi siempre puede hacer algún tipo de cierre para preservar el acceso a las variables.

De sus comentarios, si lo que está tratando de lograr es esto:

element.addEventListener('click', func(event, this.elements[i]))

Luego, puede hacer esto con una función autoejecutable (IIFE) que captura los argumentos que desea en un cierre a medida que se ejecuta y devuelve la función del controlador de eventos real:

element.addEventListener('click', (function(passedInElement) {
    return function(e) {func(e, passedInElement); };
}) (this.elements[i]), false);

Para obtener más información sobre cómo funciona un IIFE, consulte estas otras referencias:

Código de envoltura de Javascript dentro de una función anónima

Expresión de función invocada inmediatamente (IIFE) en JavaScript - Pasando jQuery

¿Cuáles son los buenos casos de uso para las funciones anónimas autoejecutables de JavaScript?

Esta última versión es quizás más fácil de ver lo que está haciendo así:

// return our event handler while capturing an argument in the closure
function handleEvent(passedInElement) {
    return function(e) {
        func(e, passedInElement); 
    };
}

element.addEventListener('click', handleEvent(this.elements[i]));

También es posible usarlo .bind()para agregar argumentos a una devolución de llamada. Cualquier argumento que pase .bind()se antepondrá a los argumentos que tendrá la devolución de llamada. Entonces, podrías hacer esto:

elem.addEventListener('click', function(a1, a2, e) {
    // inside the event handler, you have access to both your arguments
    // and the event object that the event handler passes
}.bind(elem, arg1, arg2));
jfriend00
fuente
sí, esto es casi lo que estoy tratando de hacer, pero ¿qué pasa si la función anónima se pasa como parámetro por el addClickHandler y desea darle a esta función algunos parámetros con el evento como: element.addEventListener ('click', func (event, this.elements [i]));
sebas2day
5
@ sebas2day - No puedes hacer element.addEventListener('click', func(event, this.elements[i])). Javascript simplemente no funciona de esa manera. Hay otras formas de evitar eso, usando cierres, pero no puede hacerlo de la forma en que escribió. addEventListener llama a su devolución de llamada con exactamente un argumento (el evento) y no puede cambiar eso de ninguna manera.
jfriend00
Agregué otro ejemplo de hacer esto al final de mi respuesta.
jfriend00
@ jfriend00 Lo que podrías hacer es usar el método de vinculación. function eventFunction (arg, evt) { console.log(arg[0],arg[1],arg[2]) console.log(evt) } var el = document.getElementById('elementID'); el.addEventListener('click', eventFunction.bind(el,[1,2,3])) Tenga en cuenta que el argumento del evento siempre es el último en los argumentos de la función y no se declara explícitamente en el método de vinculación.
Knight Yoshi
2
@ Bosh19: vea lo que agregué al final de mi respuesta para explicar la construcción sobre la que preguntó. Es una expresión de función invocada inmediatamente (IIFE) que es esencialmente una función declarada de forma anónima que se ejecuta inmediatamente. Es un atajo para definir una función y luego llamarla, útil cuando desea que el cuerpo esté en línea y no se llamará en ningún otro lugar, por lo que no necesita un nombre.
jfriend00
28

Es una pregunta antigua pero común. Así que déjame agregar este aquí.

Con la sintaxis de la función de flecha , puede lograrlo de una manera más sucinta, ya que está enlazado léxicamente y puede encadenarse.

Una expresión de función de flecha es una alternativa sintácticamente compacta a una expresión de función regular, aunque sin sus propios enlaces a las palabras clave this, argumentos, super o new.target.

const event_handler = (event, arg) => console.log(event, arg);
el.addEventListener('click', (event) => event_handler(event, 'An argument'));

Si necesita limpiar el detector de eventos:

// Let's use use good old function sytax
function event_handler(event, arg) {
  console.log(event, arg);
}

// Assign the listener callback to a variable
var doClick = (event) => event_handler(event, 'An argument'); 

el.addEventListener('click', doClick);

// Do some work...

// Then later in the code, clean up
el.removeEventListener('click', doClick);

Aquí hay una frase loca:

// You can replace console.log with some other callback function
el.addEventListener('click', (event) => ((arg) => console.log(event, arg))('An argument'));

Versión más dócil : más apropiada para cualquier trabajo en su sano juicio.

el.addEventListener('click', (event) => ((arg) => {
  console.log(event, arg);
})('An argument'));
snnsnn
fuente
2
Esto parece una solución razonable, pero ¿qué pasa cuando tienes que eliminar el mismo detector de eventos?
Dushyant Sabharwal
@SnnSnn: +1. Usé esto para proporcionar una propiedad de clase al controlador de eventos. Entré thisy el controlador pudo acceder a lo que necesita.
Robotron
Si mi entendimiento es correcto, en realidad estaría pasando una función anónima como su controlador de eventos: (e) => {....} y no suEventHandlerFunction (). Esto causará problemas si desea eliminar el controlador de eventos más tarde, ya que pasó una función anónima para comenzar con ???
PowerAktar
La respuesta explica claramente cómo eliminar el detector de eventos, por lo que no hay problema.
snnsnn
9

Algo que puedes probar es usar el método de vinculación, creo que esto logra lo que estabas pidiendo. Si nada más, sigue siendo muy útil.

function doClick(elem, func) {
  var diffElem = document.getElementById('some_element'); //could be the same or different element than the element in the doClick argument
  diffElem.addEventListener('click', func.bind(diffElem, elem))
}

function clickEvent(elem, evt) {
  console.log(this);
  console.log(elem); 
  // 'this' and elem can be the same thing if the first parameter 
  // of the bind method is the element the event is being attached to from the argument passed to doClick
  console.log(evt);
}

var elem = document.getElementById('elem_to_do_stuff_with');
doClick(elem, clickEvent);
Caballero Yoshi
fuente
2

Dada la actualización de la pregunta original, parece que hay problemas con el contexto ("esto") al pasar los controladores de eventos. Los conceptos básicos se explican, por ejemplo, aquí http://www.w3schools.com/js/js_function_invocation.asp

Una versión funcional simple de su ejemplo podría leer

var doClick = function(event, additionalParameter){
    // do stuff with event and this being the triggering event and caller
}

element.addEventListener('click', function(event)
{
  var additionalParameter = ...;
  doClick.call(this, event, additionalParameter );
}, false);

Consulte también Javascript call () & apply () vs bind ()?

Echsecutor
fuente
2

Respuesta corta:

x.addEventListener("click", function(e){myfunction(e, param1, param2)});

... 

function myfunction(e, param1, param1) {
    ... 
} 
usuario3093134
fuente
Esto no funciona correctamente. Si el valor de sus parámetros cambia cada vez que se establece el controlador de eventos, producirá resultados inesperados porque no se cierra sobre el valor de param1o param2. Si esos valores cambian, no se establecerán en el valor que tenían durante el tiempo que se creó el controlador de eventos. Se establecerán solo en el valor actual. Además, esto no es diferente de establecer una variable fuera del alcance de la función de escucha de eventos y hacer referencia a esa variable en el controlador de eventos, por lo que no logra pasar parámetros en absoluto.
TetraDev
Realmente no entendí tu punto, así que mi respuesta podría ser incorrecta. De todos modos, el controlador de eventos se establece una vez (no siempre, supongo, pero generalmente eso es lo que alguien quiere). En segundo lugar, esto prácticamente dice que la función del controlador es myfunction y que tiene 2 parámetros más la variable "evento". En tercer lugar, sí, funciona y los resultados son exactamente los esperados.
user3093134
No, no funciona. Lo probé exactamente como lo hizo usted y mi punto sigue siendo válido. Si param1es noodlela primera vez que se llama a addEventListener, y la segunda vez que lo llama, parames cheeseentonces cuando el detector de eventos se activa click, param1se establecerá en cheesey no noodle. Por lo tanto, no utiliza un "cierre" para recordar el valor que existía en el momento en que se agregó el detector de eventos. Hay escenarios en los que el detector de eventos debe agregarse varias veces y no es necesario ilustrarlo para que mi punto sea válido.
TetraDev
No soy un experto y no he oído hablar del "cierre" en la programación antes, así que no entiendo lo que quieres decir con eso.
user3093134
1
Gracias por verlo, definitivamente visitaré los enlaces que publicaste también. Además, debo decir que es muy educado y honesto, señor, siga así y tenga un buen día :)
user3093134
0

thisdentro de doThingsestá el objeto de la ventana. Prueba esto en su lugar:

var doThings = function (element) {
    var eventHandler = function(ev, func){
        if (element[ev] == undefined) {
            return;
        }

        element[ev] = function(e){
            func(e, element);
        }
    };

    return {
        eventHandler: eventHandler
    };
};
jbabey
fuente
-2
let obj = MyObject();

elem.someEvent( function(){ obj.func(param) } );

//calls the MyObject.func, passing the param.
Δημήτρης Βουζουναράς
fuente
param también puede ser una función de devolución de llamada (en el contexto global -say-) y, por lo tanto, se puede volver a llamar fácilmente, con los parámetros solicitados.
Δημήτρης Βουζουναράς