¿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.

Abhishek Yadav
fuente
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);
Sergey Ilinsky
fuente
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!

Zaloz
fuente
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) .

Brian Moore
fuente
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

Oleksandr Tkalenko
fuente
¡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.

tomcek112
fuente
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);

Ganesh Krishnan
fuente
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)
ahuigo
fuente
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);
Thevs
fuente
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);
Hola Mundo
fuente
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);
eclos
fuente
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

kta
fuente
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.

chovy
fuente
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);
Beto
fuente
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));
Felipe
fuente
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));
desarrollador82
fuente
3

buena alternativa de una línea

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

 //some action...

}
Gon82
fuente
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 :-)

DMike92
fuente
¿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);
     });
}
Suneel Kumar
fuente
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;)
   }
hoogw
fuente
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);
StanE
fuente
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();
Gennadiy Sherbakha
fuente
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>

Nate
fuente
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!!

Asik
fuente
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
Šerg
fuente
¿Qué demonios es esto?
NiCk Newman
0

Necesitas:

newElem.addEventListener('click', {
    handleEvent: function (event) {
        clickImg(parameter);
    }
});
Victor Behar
fuente
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
Spoo
fuente
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.

Bruce Tong
fuente
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.

AAYUSH SHAH
fuente
-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

fhuertas
fuente
-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

fnclovers
fuente