¿Cómo pasar argumentos a la función de escucha addEventListener?


304

La situación es algo así como ...

var someVar = some_other_function();
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);

El problema es que el valor de someVarno es visible dentro de la función de escucha del addEventListener, donde probablemente se trata como una nueva variable.


8
Un artículo muy claro al respecto: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita

No es la forma más limpia, pero hace el trabajo. Tenga en cuenta que si someVar solo puede ser un dígito o texto: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Ignas2526

Acabo de tener este problema hoy: la solución dada aquí es correcta (otras soluciones tienen problemas como el problema del bucle, etc.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Respuestas:


207

No hay absolutamente nada de malo en el código que ha escrito. Ambos some_functiony someVardeberían ser accesibles, en caso de que estuvieran disponibles en el contexto donde los anónimos

function() { some_function(someVar); } 

fue creado.

Compruebe si la alerta le da el valor que estaba buscando, asegúrese de que sea accesible en el ámbito de la función anónima (a menos que tenga más código que funcione en la misma someVarvariable al lado de la llamada a addEventListener)

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);

86
Esto no funciona en for loop. Siempre obtengo el último valor y no el que pertenecía a esa iteración. ¿Alguna solución?
iMatoria

66
¿Alguien sabe por qué no funciona en bucle? ¿Cuál es la razón de ese comportamiento?
Morfidon

3
@Morfidon porque la función con variables globales actúa como cierres en JavaScript, lo que significa que recuerdan el entorno fuera de su ámbito léxico. Si solo crea una función diferente en el mismo entorno, harán referencia al mismo entorno.
bugwheels94

16
@ Morfidon: en un bucle, el valor de someVar no es el valor que tenía cuando se agregó el oyente, sino el valor que tiene cuando se ejecuta el oyente. Cuando se ejecuta el escucha, el ciclo ya ha finalizado, por lo que el valor de someVar será el valor que tenía cuando finalizó el ciclo.
www.admiraalit.nl

44
@iMatoria Acabo de descubrir que crear un método bound functionutilizando este .bind()método resolverá el problema con los bucles developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien

354

¿Por qué no solo obtener los argumentos del atributo de destino del evento?

Ejemplo:

const someInput = document.querySelector('button');
someInput.addEventListener('click', myFunc, false);
someInput.myParam = 'This is my parameter';
function myFunc(evt)
{
  window.alert(evt.currentTarget.myParam);
}
<button class="input">Show parameter</button>

JavaScript es un lenguaje orientado a prototipos, ¡recuerda!


16
Esta es la respuesta correcta porque nos permite usar después de la función 'removeEventListener'.
user5260143

14
¿No debería ser evt.currentTarget.myParam? Si hay otro elemento dentro de 'someInput', evt.targetpuede referirse al elemento interno. ( jsfiddle.net/qp5zguay/1 )
Herbertusz

Esto conserva this! El uso de este método en mecanografiado requiere que el elemento sea anyo para hacer un subtipo.
Old Badman Grey el

1
Mis variables siguen regresando como indefinidas ... ¿Alguna idea sobre cómo solucionarlo?
nomaam

1
Si addEventListeneres para document, evt.target.myParamno funcionó para mí. Tuve que usar evt.currentTarget.myParamen su lugar.
turrican_34

67

Esta pregunta es antigua, pero pensé que ofrecería una alternativa usando .bind () de ES5 - para la posteridad. :)

function some_func(otherFunc, ev) {
    // magic happens
}
someObj.addEventListener("click", some_func.bind(null, some_other_func), false);

Solo tenga en cuenta que necesita configurar su función de escucha con el primer parámetro como argumento que está pasando a vincular (su otra función) y el segundo parámetro es ahora el evento (en lugar del primero, como hubiera sido) .


1
Function.prototype.bind () es realmente la mejor manera de resolver este problema. Además, funciona intuitivamente dentro de los bucles: obtienes el alcance léxico que deseas. No hay funciones anónimas, IIFE o propiedades especiales añadidas a los objetos.
Clint Pachl

Vea los pros y los contras de IIFE vs bind () .
Clint Pachl

1
Al usar Function.prototype.bind()no puede eliminar el detector de eventos , es mejor usar una función de curry en su lugar (ver respuesta @ tomcek112)
pldg

NOTA: some_other_funces una variable, puede pasar el valor que desee some_func.
hitautodestruct

28

Puede vincular todos los argumentos necesarios con 'vincular':

root.addEventListener('click', myPrettyHandler.bind(null, event, arg1, ... ));

De esta manera siempre obtendrá el event, arg1y otras cosas pasó a myPrettyHandler.

http://passy.svbtle.com/partial-application-in-javascript-using-bind


¡Gracias! Ya lo había intentado .bind()pero sin el nulo como primer param. que no funcionó
Larphoid

no es necesario null, funciona bien con .bind(event, arg1), al menos con VueJS.
DevonDahon

27

Pregunta bastante antigua, pero tuve el mismo problema hoy. La solución más limpia que encontré es usar el concepto de curry.

El código para eso:

someObj.addEventListener('click', some_function(someVar));

var some_function = function(someVar) {
    return function curried_func(e) {
        // do something here
    }
}

Al nombrar la función curry, le permite llamar a Object.removeEventListener para anular el registro de eventListener en un momento de ejecución posterior.


44
Me alegra encontrar esta respuesta que menciona la función curry. ¿Cómo eliminarías el detector de eventos?
bob

3
Impresionante ver buena terminología. Debería poder eliminar el detector de eventos nombrando la función curry. Propondré una edición.
Matthew Brent

Esta respuesta registrará la función tantas veces como se llame a addEventListener, ya que alguna_función (var) devuelve una función recién creada cada vez.
Yahia

no me gusta la idea de tener que nombrar la función curry para eliminar el oyente porque entonces estás tratando con 2 espacios de nombres diff que
debes

19

Puede agregar y eliminar oyentes de eventos con argumentos declarando una función como variable.

myaudio.addEventListener('ended',funcName=function(){newSrc(myaudio)},false);

newSrces el método con myaudio como parámetro funcNamees la variable de nombre de función

Puede eliminar el oyente con myaudio.removeEventListener('ended',func,false);


12

Puede pasar somevar por valor (no por referencia) a través de una función de JavaScript conocida como cierre :

var someVar='origin';
func = function(v){
    console.log(v);
}
document.addEventListener('click',function(someVar){
   return function(){func(someVar)}
}(someVar));
someVar='changed'

O podría escribir una función de ajuste común como wrapEventCallback:

function wrapEventCallback(callback){
    var args = Array.prototype.slice.call(arguments, 1);
    return function(e){
        callback.apply(this, args)
    }
}
var someVar='origin';
func = function(v){
    console.log(v);
}
document.addEventListener('click',wrapEventCallback(func,someVar))
someVar='changed'

Aquí wrapEventCallback(func,var1,var2)es como:

func.bind(null, var1,var2)

1
Muchas gracias por esta respuesta! El OP no estaba buscando esto, pero creo que las personas que escriben "Cómo pasar argumentos para agregarEventListener" en Google estarán buscando su respuesta. Solo necesita un poco más de explicación :) Lo estoy editando.
Sindarus

9

someVarEl valor debe ser accesible solo en some_function()contexto, no desde el oyente. Si le gusta tenerlo dentro del oyente, debe hacer algo como:

someObj.addEventListener("click",
                         function(){
                             var newVar = someVar;
                             some_function(someVar);
                         },
                         false);

y usar newVaren su lugar.

La otra forma es devolver el someVarvalor de some_function()para usarlo más en listener (como una nueva var local):

var someVar = some_function(someVar);

9

Aquí hay otra forma (esta funciona dentro de los bucles):

var someVar = some_other_function();
someObj.addEventListener("click", 

function(theVar){
    return function(){some_function(theVar)};
}(someVar),

false);

2
Esta es la mejor manera. Feo, pero efectivo dentro de los bucles ya que al enviar un argumento a una función anónima capturará la var.
bob

9

Function.prototype.bind () es la forma de vincular una función de destino a un ámbito particular y, opcionalmente, definir el thisobjeto dentro de la función de destino.

someObj.addEventListener("click", some_function.bind(this), false);

O para capturar parte del alcance léxico, por ejemplo en un bucle:

someObj.addEventListener("click", some_function.bind(this, arg1, arg2), false);

Finalmente, si el thisparámetro no es necesario dentro de la función de destino:

someObj.addEventListener("click", some_function.bind(null, arg1, arg2), false);

7

Utilizar

   el.addEventListener('click',
    function(){
        // this will give you the id value 
        alert(this.id);    
    },
false);

Y si desea pasar cualquier valor personalizado a esta función anónima, entonces la forma más fácil de hacerlo es

 // this will dynamically create property a property
 // you can create anything like el.<your  variable>
 el.myvalue = "hello world";
 el.addEventListener('click',
    function(){
        //this will show you the myvalue 
        alert(el.myvalue);
        // this will give you the id value 
        alert(this.id);    
    },
false);

Funciona perfectamente en mi proyecto. Espero que esto ayude


Sí, definitivamente ayudó, ya que también mantuvo el alcance esperado dentro de un forciclo.
j4v1

4
    $form.addEventListener('submit', save.bind(null, data, keyword, $name.value, myStemComment));
    function save(data, keyword, name, comment, event) {

Así es como obtuve el evento pasado correctamente.


Excelente, así es como estaba casi concluyendo: simplemente pasé por error un evento adicional en bind cuando no está allí (como en angular), que viene automáticamente en este caso.
Manohar Reddy Poreddy

3

Enviar argumentos a una función de devolución de llamada de eventListener requiere crear una función aislada y pasar argumentos a esa función aislada.

Aquí hay una pequeña y agradable función de ayuda que puedes usar. Basado en el ejemplo anterior de "Hola mundo".)

Una cosa que también se necesita es mantener una referencia a la función para que podamos eliminar el oyente limpiamente.

// Lambda closure chaos.
//
// Send an anonymous function to the listener, but execute it immediately.
// This will cause the arguments are captured, which is useful when running 
// within loops.
//
// The anonymous function returns a closure, that will be executed when 
// the event triggers. And since the arguments were captured, any vars 
// that were sent in will be unique to the function.

function addListenerWithArgs(elem, evt, func, vars){
    var f = function(ff, vv){
            return (function (){
                ff(vv);
            });
    }(func, vars);

    elem.addEventListener(evt, f);

    return f;
}

// Usage:

function doSomething(withThis){
    console.log("withThis", withThis);
}

// Capture the function so we can remove it later.
var storeFunc = addListenerWithArgs(someElem, "click", doSomething, "foo");

// To remove the listener, use the normal routine:
someElem.removeEventListener("click", storeFunc);

Esta respuesta es del '15, pero es exactamente lo que necesitaba para manejar este problema con el uso de un gancho useRef. Si está utilizando un gancho de referencia y necesita un oyente para que pueda limpiar el desmontaje de componentes, este es el lugar. El 4to argumento storeFuncdebería ser tu variable de referencia. Use la eliminación de su escucha en un efecto como este y estará listo:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Una forma es hacer esto con una función externa :

elem.addEventListener('click', (function(numCopy) {
  return function() {
    alert(numCopy)
  };
})(num));

Este método de ajustar una función anónima entre paréntesis y llamarla de inmediato se llama IIFE (Expresión de función invocada inmediatamente)

Puede consultar un ejemplo con dos parámetros en http://codepen.io/froucher/pen/BoWwgz .

catimg.addEventListener('click', (function(c, i){
  return function() {
    c.meows++;
    i.textContent = c.name + '\'s meows are: ' + c.meows;
  }
})(cat, catmeows));

3

Si no me equivoco al llamar a la función con, en bindrealidad se crea una nueva función que el bindmétodo devuelve . Esto le causará problemas más adelante o si desea eliminar el detector de eventos, ya que es básicamente como una función anónima:

// Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', myCallback);
someObject.removeEventListener('event', myCallback);

// Not Possible:
function myCallback() { /* code here */ }
someObject.addEventListener('event', function() { myCallback });
someObject.removeEventListener('event', /* can't remove anonymous function */);

Así que tenlo en cuenta.

Si está utilizando ES6, podría hacer lo mismo que se sugiere pero un poco más limpio:

someObject.addEventListener('event', () => myCallback(params));

3

buena alternativa de una línea

element.addEventListener('dragstart',(evt) => onDragStart(param1, param2, param3, evt));
function onDragStart(param1, param2, param3, evt) {

 //some action...

}

2

Pruebe también estos (IE8 + Chrome. No sé para FF):

function addEvent(obj, type, fn) {
    eval('obj.on'+type+'=fn');
}

function removeEvent(obj, type) {
    eval('obj.on'+type+'=null');
}

// Use :

function someFunction (someArg) {alert(someArg);}

var object=document.getElementById('somObject_id') ;
var someArg="Hi there !";
var func=function(){someFunction (someArg)};

// mouseover is inactive
addEvent (object, 'mouseover', func);
// mouseover is now active
addEvent (object, 'mouseover');
// mouseover is inactive

Espero que no haya errores tipográficos :-)


¿Qué tan difícil sería dar una respuesta completa? ¿Debo probar esto en FF? Bueno, no me molestaré ...
StefanNch

2

Estaba atrapado en esto porque lo estaba usando en un bucle para encontrar elementos y agregarle una lista. Si lo está usando en un bucle, esto funcionará perfectamente

for (var i = 0; i < states_array.length; i++) {
     var link = document.getElementById('apply_'+states_array[i].state_id);
     link.my_id = i;
     link.addEventListener('click', function(e) {   
        alert(e.target.my_id);        
        some_function(states_array[e.target.my_id].css_url);
     });
}

2

En 2019, muchos cambios en la API, la mejor respuesta ya no funciona, sin corregir el error.

compartir un código de trabajo

Inspirado por toda la respuesta anterior.

 button_element = document.getElementById('your-button')

 button_element.setAttribute('your-parameter-name',your-parameter-value);

 button_element.addEventListener('click', your_function);


 function your_function(event)
   {
      //when click print the parameter value 
      console.log(event.currentTarget.attributes.your-parameter-name.value;)
   }

1

Hay una variable especial dentro de todas las funciones: argumentos . Puede pasar sus parámetros como parámetros anónimos y acceder a ellos (por orden) a través de la variable de argumentos .

Ejemplo:

var someVar = some_other_function();
someObj.addEventListener("click", function(someVar){
    some_function(arguments[0]);
}, false);

Hmm ... ¿Cuál es la razón del voto negativo? Si no era lo que estaba buscando, explique más claramente lo que quiere decir (sé que la pregunta ya ha sido respondida). ¿Pero mi código no responde lo que pediste? La variable especial "argumentos" le da acceso a todos los parámetros dentro de una función.
StanE

1
    var EV = {
        ev: '',
        fn: '',
        elem: '',
        add: function () {
            this.elem.addEventListener(this.ev, this.fn, false);
        }
    };

    function cons() {
        console.log('some what');
    }

    EV.ev = 'click';
    EV.fn = cons;
    EV.elem = document.getElementById('body');
    EV.add();

//If you want to add one more listener for load event then simply add this two lines of code:

    EV.ev = 'load';
    EV.add();

1

El siguiente enfoque funcionó bien para mí. Modificado desde aquí .

function callback(theVar) {
  return function() {
    theVar();
  }
}

function some_other_function() {
  document.body.innerHTML += "made it.";
}

var someVar = some_other_function;
document.getElementById('button').addEventListener('click', callback(someVar));
<!DOCTYPE html>
<html>
  <body>
    <button type="button" id="button">Click Me!</button>
  </body>
</html>


0

La siguiente respuesta es correcta, pero el código siguiente no funciona en IE8 si se supone que comprimió el archivo js usando yuicompressor. (De hecho, todavía la mayoría de los pueblos de EE. UU. Usan IE8)

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click",
                         function(){
                          some_function(someVar);
                         },
                         false);

Entonces, podemos solucionar el problema anterior de la siguiente manera y funciona bien en todos los navegadores

var someVar, eventListnerFunc;
someVar = some_other_function();
eventListnerFunc = some_function(someVar);
someObj.addEventListener("click", eventListnerFunc, false);

Espero que sea útil para alguien que esté comprimiendo el archivo js en un entorno de producción.

¡¡Buena suerte!!


0

El siguiente código funcionó bien para mí (firefox):

for (var i=0; i<3; i++) {
   element = new ...   // create your element
   element.counter = i;
   element.addEventListener('click', function(e){
        console.log(this.counter);
        ...            // another code with this element
   }, false);
}

Salida:

0
1
2

¿Qué demonios es esto?
NiCk Newman

0

Necesitas:

newElem.addEventListener('click', {
    handleEvent: function (event) {
        clickImg(parameter);
    }
});

0

Probablemente no sea óptimo, pero lo suficientemente simple para aquellos que no son muy expertos. Coloque la función que llama a addEventListener en su propia función. De esa forma, cualquier valor de función que se le pase mantenga su propio alcance y puede iterar sobre esa función tanto como desee.

Ejemplo Trabajé con la lectura de archivos, ya que necesitaba capturar y representar una vista previa de la imagen y el nombre del archivo. Me llevó un tiempo evitar problemas asincrónicos al utilizar un tipo de carga de múltiples archivos. Accidentalmente vería el mismo 'nombre' en todos los renders a pesar de cargar diferentes archivos.

Originalmente, toda la función readFile () estaba dentro de la función readFiles (). Esto causó problemas de alcance asincrónicos.

    function readFiles(input) {
      if (input.files) {
        for(i=0;i<input.files.length;i++) {

          var filename = input.files[i].name;

          if ( /\.(jpe?g|jpg|png|gif|svg|bmp)$/i.test(filename) ) {
            readFile(input.files[i],filename);
          }
       }
      }
    } //end readFiles



    function readFile(file,filename) {
            var reader = new FileReader();

            reader.addEventListener("load", function() { alert(filename);}, false);

            reader.readAsDataURL(file);

    } //end readFile

0

solo me gustaría agregar. si alguien está agregando una función que actualiza las casillas de verificación a un detector de eventos, debería usar en event.targetlugar de thisactualizar las casillas de verificación.


0

Tengo un enfoque muy simplista. Esto puede funcionar para otros, ya que me ayudó. Es ... Cuando tiene múltiples elementos / variables asignados a una misma función y desea pasar la referencia, la solución más simple es ...

function Name()
{

this.methodName = "Value"

}

Eso es. Funcionó para mi. Tan sencillo.


-1

Otra alternativa, quizás no tan elegante como el uso de bind, pero es válida para eventos en un bucle

for (var key in catalog){
    document.getElementById(key).my_id = key
    document.getElementById(key).addEventListener('click', function(e) {
        editorContent.loadCatalogEntry(e.srcElement.my_id)
    }, false);
}

Ha sido probado para las extensiones de Google Chrome y quizás e.srcElement debe ser reemplazado por e.source en otros navegadores

Encontré esta solución usando el comentario publicado por Imatoria pero no puedo marcarlo como útil porque no tengo suficiente reputación: D


-1

Esta solución puede ser buena para mirar

var some_other_function = someVar => function() {
}

someObj.addEventListener('click', some_other_function(someVar));

o atar valientes también será bueno

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.