Comment passer des arguments à la fonction d'écoute addEventListener?


304

La situation ressemble un peu à

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

Le problème est que la valeur de someVarn'est pas visible dans la fonction d'écoute de la addEventListener, où elle est probablement traitée comme une nouvelle variable.


8
Un article très clair sur le sujet: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita

Pas de la manière la plus propre, mais fait le travail. Notez que si someVar ne peut être qu'un chiffre ou du texte: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Ignas2526

Je viens d'avoir ce problème aujourd'hui - la solution donnée ici est correcte (d'autres solutions ont des problèmes comme le problème de boucle, etc.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Réponses:


207

Il n'y a absolument rien de mal avec le code que vous avez écrit. Les deux some_functionet someVardevraient être accessibles, au cas où ils étaient disponibles dans le contexte où anonyme

function() { some_function(someVar); } 

a été créé.

Vérifiez si l'alerte vous donne la valeur que vous recherchez, assurez-vous qu'elle sera accessible dans le cadre de la fonction anonyme (sauf si vous avez plus de code qui fonctionne sur la même someVarvariable à côté de l'appel à addEventListener)

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

86
Cela ne fonctionne pas dans la boucle. J'obtiens toujours la dernière valeur et non celle qui appartenait à cette itération. Toute solution?
iMatoria

6
Quelqu'un sait pourquoi cela ne fonctionne pas en boucle? Quelle est la raison de ce comportement?
Morfidon

3
@Morfidon parce que la fonction avec des variables globales agit comme des fermetures en javascript ce qui signifie qu'elles se souviennent de l'environnement en dehors de leur portée lexicale. Si vous créez simplement une fonction différente dans le même environnement, elles feront référence au même environnement.
bugwheels94

16
@Morfidon: Dans une boucle, la valeur de someVar n'est pas la valeur qu'elle avait lorsque l'écouteur a été ajouté, mais la valeur qu'elle a lorsque l'écouteur est exécuté. Lorsque l'écouteur est exécuté, la boucle est déjà terminée, donc la valeur de someVar sera la valeur qu'elle avait à la fin de la boucle.
www.admiraalit.nl du

4
@iMatoria Je viens de découvrir que la création d'un bound functionutilisant la .bind()méthode résoudra le problème avec les boucles developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien

354

Pourquoi ne pas simplement récupérer les arguments de l'attribut cible de l'événement?

Exemple:

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 est un langage orienté prototype, rappelez-vous!


16
C'est la bonne réponse, car elle nous a permis d'utiliser après la fonction «removeEventListener».
user5260143

14
N'est-ce pas evt.currentTarget.myParam? S'il y a un autre élément à l'intérieur de 'someInput', il evt.targetpeut s'agir de l'élément interne. ( jsfiddle.net/qp5zguay/1 )
Herbertusz

Cela préserve this! L'utilisation de cette méthode en tapuscrit nécessite cependant que l'élément soit anyou fasse un sous-type.
Old Badman Gray

1
Mes variables reviennent comme indéfinies ... des réflexions sur la façon de résoudre ce problème?
nomaam

1
Si addEventListenerc'est pour document, evt.target.myParamça n'a pas marché pour moi. J'ai dû utiliser à la evt.currentTarget.myParamplace.
turrican_34

67

Cette question est ancienne mais je pensais offrir une alternative en utilisant .bind () d'ES5 - pour la postérité. :)

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

Sachez simplement que vous devez configurer votre fonction d'écoute avec le premier paramètre comme argument que vous passez dans bind (votre autre fonction) et le deuxième paramètre est maintenant l'événement (au lieu du premier, comme il l'aurait été) .


1
Function.prototype.bind () est vraiment le meilleur moyen de résoudre ce problème. De plus, il fonctionne intuitivement à l'intérieur des boucles - vous obtenez la portée lexicale souhaitée. Aucune fonction anonyme, IIFE ou propriété spéciale collée aux objets.
Clint Pachl

Voir les avantages et les inconvénients de IIFE vs bind () .
Clint Pachl

1
En utilisant, Function.prototype.bind()vous ne pouvez pas supprimer l'écouteur d'événements , mieux vaut utiliser une fonction de curry à la place (voir la réponse @ tomcek112)
pldg

REMARQUE: some_other_funcest une variable, vous pouvez transmettre la valeur que vous souhaitez some_func.
hitautodestruct

28

Vous pouvez simplement lier tous les arguments nécessaires avec 'bind':

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

De cette façon , vous aurez toujours la event, arg1et d' autres choses à passer myPrettyHandler.

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


Merci! Avait déjà essayé .bind()mais sans le null comme premier param. qui n'a pas fonctionné.
Larphoid

pas besoin null, ça marche très bien avec .bind(event, arg1), au moins avec VueJS.
DevonDahon

27

Assez et vieille question, mais j'ai eu le même problème aujourd'hui. La solution la plus propre que j'ai trouvée est d'utiliser le concept de curry.

Le code pour cela:

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

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

En nommant la fonction curry, elle vous permet d'appeler Object.removeEventListener pour désenregistrer eventListener lors d'une exécution ultérieure.


4
Heureux de rencontrer cette réponse mentionnant la fonction curry. Comment supprimeriez-vous l'écouteur d'événements?
bob

3
Génial de voir une bonne terminologie. Vous devriez pouvoir supprimer l'écouteur d'événement en nommant la fonction curry. je vais proposer un montage.
Matthew Brent

Cette réponse enregistrera la fonction autant de fois que addEventListener est appelée, car some_function (var) renvoie à chaque fois une fonction nouvellement créée.
Yahia

je n'aime pas l'idée de devoir nommer la fonction curry afin de supprimer l'auditeur
parce

19

Vous pouvez ajouter et supprimer des écouteurs d'événements avec des arguments en déclarant une fonction en tant que variable.

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

newSrcest la méthode avec myaudio comme paramètre funcNameest la variable de nom de fonction

Vous pouvez supprimer l'auditeur avec myaudio.removeEventListener('ended',func,false);


12

Vous pouvez passer une valeur par valeur (et non par référence) via une fonctionnalité javascript appelée fermeture :

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

Ou vous pouvez écrire une fonction d'habillage courante telle que 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'

Voici wrapEventCallback(func,var1,var2)comme:

func.bind(null, var1,var2)

1
Merci beaucoup pour cette réponse! L'OP ne cherchait pas cela, mais je pense que les gens qui tapent "Comment passer des arguments à addEventListener" dans google chercheront votre réponse. Il a juste besoin d'un peu plus d'explications :) Je le modifie.
Sindarus

9

someVarLa valeur ne doit être accessible que dans le some_function()contexte, pas à partir de l'auditeur. Si vous aimez l'avoir dans l'auditeur, vous devez faire quelque chose comme:

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

et utilisez à la newVarplace.

L'autre façon est de renvoyer la someVarvaleur de some_function()pour l'utiliser davantage dans l'écouteur (en tant que nouvelle variable locale):

var someVar = some_function(someVar);

9

Voici encore une autre façon (celle-ci fonctionne à l'intérieur pour les boucles):

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

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

false);

2
C'est la meilleur façon. Moche, mais efficace dans les boucles car en envoyant un argument dans une fonction anonyme, il capturera la var.
bob

9

Function.prototype.bind () est le moyen de lier une fonction cible à une portée particulière et éventuellement de définir l' thisobjet dans la fonction cible.

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

Ou pour capturer une partie de la portée lexicale, par exemple dans une boucle:

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

Enfin, si le thisparamètre n'est pas nécessaire dans la fonction cible:

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

7

Utilisation

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

Et si vous souhaitez transmettre une valeur personnalisée à cette fonction anonyme, la façon la plus simple de le faire est

 // 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);

Fonctionne parfaitement dans mon projet. J'espère que cela vous aidera


Oui, certainement aidé, car il a également maintenu la portée attendue dans une forboucle.
j4v1

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

C'est ainsi que l'événement s'est déroulé correctement.


Excellent, c'est comme ça que je concluais presque - je viens de passer un événement supplémentaire à tort quand il n'est pas là (comme dans angulaire), ce qui arrive automatiquement dans ce cas.
Manohar Reddy Poreddy

3

L'envoi d'arguments à la fonction de rappel d'un eventListener nécessite la création d'une fonction isolée et la transmission d'arguments à cette fonction isolée.

Voici une jolie petite fonction d'aide que vous pouvez utiliser. Basé sur l'exemple "bonjour le monde" ci-dessus.)

Une chose qui est également nécessaire est de maintenir une référence à la fonction afin que nous puissions supprimer l'auditeur proprement.

// 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);

Cette réponse est de '15 mais c'est exactement ce dont j'avais besoin pour gérer ce problème avec l'utilisation d'un hook useRef. Si vous utilisez un hook ref et que vous aviez besoin d'un écouteur pour le nettoyer lors du démontage des composants, c'est tout. Le 4ème argument storeFuncdoit être votre variable ref. Mettez votre suppression d'auditeur dans un effet comme celui-ci et vous êtes prêt à partir:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Une façon de faire cela avec une fonction externe :

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

Cette méthode d'encapsuler une fonction anonyme entre parenthèses et de l'appeler immédiatement est appelée IIFE (expression de fonction immédiatement invoquée)

Vous pouvez consulter un exemple avec deux paramètres dans 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 je ne me trompe pas, l'utilisation de l'appel de la fonction avec bindcrée en fait une nouvelle fonction qui est retournée par la bindméthode. Cela vous posera des problèmes plus tard ou si vous souhaitez supprimer l'écouteur d'événements, car il s'agit essentiellement d'une fonction anonyme:

// 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 */);

Alors, gardez cela à l'esprit.

Si vous utilisez ES6, vous pouvez faire la même chose que suggéré, mais un peu plus propre:

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

3

belle alternative à une ligne

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

 //some action...

}

2

Essayez également ces derniers (IE8 + Chrome. Je ne sais pas pour 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

J'espère qu'il n'y a pas de fautes de frappe :-)


Serait-il difficile de mettre une réponse complète? Dois-je tester cela sur FF? Eh bien, je ne vais pas déranger ...
StefanNch

2

J'étais coincé dans cela car je l'utilisais en boucle pour trouver des éléments et y ajouter un listeur. Si vous l'utilisez en boucle, cela fonctionnera parfaitement

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, beaucoup de changements dans l'API, la meilleure réponse ne fonctionne plus, sans correction de bug.

partager du code de travail.

Inspiré par toutes les réponses ci-dessus.

 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

Il y a une variable spéciale à l'intérieur de toutes les fonctions: les arguments . Vous pouvez passer vos paramètres en tant que paramètres anonymes et y accéder (par ordre) via la variable arguments .

Exemple:

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

Hmm ... Quelle est la raison du downvote? Si ce n'était pas ce que vous cherchiez, veuillez expliquer plus clairement ce que vous voulez dire (je sais que la question a déjà été répondue). Mais mon code ne répond-il pas à ce que vous avez demandé? La variable spéciale "arguments" vous donne accès à tous les paramètres d'une fonction.
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

L'approche suivante a bien fonctionné pour moi. Modifié à partir d' ici .

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 réponse suivante est correcte mais le code ci-dessous ne fonctionne pas dans IE8 si supposons que vous avez compressé le fichier js à l'aide de yuicompressor. (En fait, la plupart des peuples américains utilisent encore IE8)

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

Donc, nous pouvons résoudre le problème ci-dessus comme suit et cela fonctionne bien dans tous les navigateurs

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

J'espère que cela serait utile pour quelqu'un qui compresse le fichier js dans un environnement de production.

Bonne chance!!


0

Le code suivant a bien fonctionné pour moi (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);
}

Production:

0
1
2

Qu'est-ce que c'est que ça dans le monde?
NiCk Newman

0

Vous avez besoin:

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

0

Probablement pas optimal, mais assez simple pour ceux qui ne sont pas super avertis. Placez la fonction qui appelle addEventListener dans sa propre fonction. De cette façon, toutes les valeurs de fonction qui y sont passées conservent leur propre étendue et vous pouvez parcourir cette fonction autant que vous le souhaitez.

Exemple J'ai travaillé avec la lecture de fichiers car j'avais besoin de capturer et de rendre un aperçu de l'image et du nom de fichier. Il m'a fallu un certain temps pour éviter les problèmes asynchrones lors de l'utilisation d'un type de téléchargement de fichiers multiples. Je verrais accidentellement le même «nom» sur tous les rendus malgré le téléchargement de fichiers différents.

À l'origine, toute la fonction readFile () se trouvait dans la fonction readFiles (). Cela a provoqué des problèmes de portée asynchrone.

    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

voudrais juste ajouter. si quelqu'un ajoute une fonction qui met à jour les cases à cocher vers un écouteur d'événements, vous devrez utiliser event.targetau lieu de thismettre à jour les cases à cocher.


0

J'ai une approche très simpliste. Cela peut fonctionner pour d'autres car cela m'a aidé. C'est ... Lorsque vous avez plusieurs éléments / variables affectés à une même fonction et que vous souhaitez passer la référence, la solution la plus simple est ...

function Name()
{

this.methodName = "Value"

}

C'est tout. Ça a marché pour moi. Si simple.


-1

Autre alternative, peut-être pas aussi élégante que l'utilisation de bind, mais elle est valable pour les événements en boucle

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);
}

Il a été testé pour les extensions google chrome et peut-être que e.srcElement doit être remplacé par e.source dans d'autres navigateurs

J'ai trouvé cette solution en utilisant le commentaire publié par Imatoria mais je ne peux pas la marquer comme utile car je n'ai pas assez de réputation: D


-1

Cette solution peut être bonne pour la recherche

var some_other_function = someVar => function() {
}

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

ou lier des valeurs sera également bon

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.