Gestionnaire d'événements Javascript avec paramètres


89

Je veux créer un eventHandler qui transmet l'événement et certains paramètres. Le problème est que la fonction n'obtient pas l'élément. Voici un exemple:

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

L'élément doit être défini en dehors de la fonction anonyme. Comment puis-je utiliser l'élément passé dans la fonction anonyme? Y a-t-il un moyen de faire cela?

Et qu'en est-il de addEventListener? Je ne semble pas du tout capable de transmettre l'événement via un addEventListener, n'est-ce pas?

Mise à jour

J'ai semblé résoudre le problème avec 'ceci'

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

Où cela contient cet élément auquel je peux accéder dans la fonction.

Le addEventListener

Mais je m'interroge sur addEventListener:

function doClick(elem, func){
    element.addEventListener('click', func(event, elem), false);
}

2
Vous avez des éléments, des éléments, des éléments impliqués dans votre question. Pouvez-vous être moins déroutant?
mihai

Je suis désolé, j'essayais de faire un exemple de ma situation mais cela ne s'est pas bien passé, j'ai mis à jour le code.
sebas2day

Cherchez-vous e.target/ e.currentTarget?
Rolf

1
comment envoyer un paramètre si je l'utilise: ok.addEventListener ("cliquez", this.changeText.bind (this));
Alberto Acuña

Réponses:


103

Je ne comprends pas exactement ce que votre code essaie de faire, mais vous pouvez rendre des variables disponibles dans n'importe quel gestionnaire d'événements en utilisant les avantages des fermetures de fonctions:

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

En fonction de votre situation de codage exacte, vous pouvez à peu près toujours faire une sorte de fermeture pour préserver l'accès aux variables pour vous.

D'après vos commentaires, si ce que vous essayez d'accomplir est ceci:

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

Ensuite, vous pouvez le faire avec une fonction auto-exécutable (IIFE) qui capture les arguments souhaités dans une fermeture lors de son exécution et renvoie la fonction de gestionnaire d'événements réelle:

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

Pour plus d'informations sur le fonctionnement d'un IIFE, consultez ces autres références:

Code d'emballage Javascript dans une fonction anonyme

Expression de fonction immédiatement invoquée (IIFE) dans JavaScript - Passer jQuery

Quels sont les bons cas d'utilisation des fonctions anonymes auto-exécutables JavaScript?

Cette dernière version est peut-être plus facile de voir ce qu'elle fait comme ceci:

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

Il est également possible de l'utiliser .bind()pour ajouter des arguments à un rappel. Tous les arguments que vous passez .bind()seront ajoutés aux arguments que le rappel lui-même aura. Donc, vous pouvez faire ceci:

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

oui c'est presque ce que j'essaie de faire, mais que se passe-t-il si la fonction anonyme est passée en paramètre par le addClickHandler et que vous voulez donner à cette fonction des paramètres avec l'événement comme: element.addEventListener ('click', func (event, this.elements [i]));
sebas2day

5
@ sebas2day - Vous ne pouvez pas faire element.addEventListener('click', func(event, this.elements[i])). Javascript ne fonctionne tout simplement pas de cette façon. Il existe d'autres moyens de contourner cela, en utilisant des fermetures, mais vous ne pouvez pas le faire comme vous l'avez écrit. addEventListener appelle son callback avec exactement un argument (l'événement) et vous ne pouvez en aucun cas changer cela.
jfriend00

J'ai ajouté un autre exemple de faire cela à la fin de ma réponse.
jfriend00

@ jfriend00 Vous pouvez utiliser la méthode bind. 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])) Notez que l'argument d'événement est toujours le dernier dans les arguments de la fonction, et n'est pas explicitement déclaré dans la méthode de liaison.
Knight Yoshi

2
@ Bosh19 - voyez ce que j'ai ajouté à la fin de ma réponse pour expliquer la construction que vous avez posée. C'est une expression de fonction immédiatement invoquée (IIFE) qui est essentiellement une fonction déclarée de manière anonyme qui est immédiatement exécutée. C'est un raccourci pour définir une fonction puis l'appeler - utile lorsque vous voulez que le corps soit en ligne et qu'il ne sera appelé nulle part ailleurs, donc il n'a pas besoin d'un nom.
jfriend00

28

C'est une question ancienne mais courante. Alors laissez-moi ajouter celui-ci ici.

Avec la syntaxe de la fonction flèche, vous pouvez y parvenir de manière plus succincte car elle est liée lexicalement et peut être chaînée.

Une expression de fonction de flèche est une alternative syntaxiquement compacte à une expression de fonction régulière, bien que sans ses propres liaisons aux mots clés this, arguments, super ou new.target.

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

Si vous devez nettoyer l'écouteur d'événements:

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

Voici un one-liner fou:

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

Version plus docile : plus appropriée pour tout travail sensé.

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

2
Cela semble être une solution raisonnable, mais qu'en est-il lorsque vous devez supprimer le même écouteur d'événements?
Dushyant Sabharwal

@SnnSnn: +1. J'ai utilisé ceci pour fournir une propriété de classe au gestionnaire d'événements. Je viens de passer thiset le gestionnaire a pu accéder à ce dont il a besoin.
Robotron

Si je comprends bien, vous passeriez en fait une fonction anonyme comme gestionnaire d'événements: (e) => {....} et non yourEventHandlerFunction (). Cela posera des problèmes si vous souhaitez supprimer le gestionnaire d'événements plus tard, car vous avez passé une fonction anonyme pour commencer par ???
PowerAktar le

La réponse explique clairement comment supprimer l'écouteur d'événement, donc pas de problème.
snnsnn le

9

Quelque chose que vous pouvez essayer est d'utiliser la méthode bind, je pense que cela répond à vos attentes. Si rien d'autre, c'est toujours très utile.

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

2

Compte tenu de la mise à jour de la question d'origine, il semble qu'il y ait un problème avec le contexte ("this") lors du passage des gestionnaires d'événements. Les bases sont expliquées par exemple ici http://www.w3schools.com/js/js_function_invocation.asp

Une version de travail simple de votre exemple pourrait lire

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

Voir aussi Javascript call () & apply () vs bind ()?


2

Réponse courte:

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

... 

function myfunction(e, param1, param1) {
    ... 
} 

Cela ne fonctionne pas correctement. Si la valeur de vos paramètres change chaque fois que le gestionnaire d'événements est défini, cela produira des résultats inattendus car il ne se ferme pas sur la valeur de param1ou param2. Si ces valeurs changent, elles ne seront pas définies sur la valeur qu'elles étaient au moment de la création du gestionnaire d'événements. Ils seront définis uniquement sur la valeur actuelle. En outre, cela n'est pas différent de définir une variable en dehors de la portée de la fonction d'écoute d'événements et de référencer cette variable dans le gestionnaire d'événements, de sorte qu'il n'accomplit pas du tout la transmission de paramètres.
TetraDev

Je n'ai pas vraiment compris votre point, donc ma réponse est peut-être erronée. Quoi qu'il en soit, le gestionnaire d'événements est défini une fois (pas toujours je suppose, mais c'est généralement ce que quelqu'un veut). Deuxièmement, cela dit à peu près que la fonction de gestionnaire est ma fonction et qu'elle a 2 paramètres plus la variable "événement". Troisièmement, oui, cela fonctionne et les résultats sont exactement comme prévu.
user3093134

Non, ça ne marche pas. Je l'ai testé exactement comme vous l'avez fait et mon argument reste valable. Si param1c'est noodlela première fois que l'addEventListener est appelé, et la deuxième fois que vous l'appelez, paramc'est à cheesechaque fois que l'écouteur d'événement se clickdéclenche, param1sera défini sur cheeseet non noodle. Par conséquent, il n'utilise pas de «fermeture» pour se souvenir de la valeur qui existait au moment de l'ajout de l'écouteur d'événements. Il existe des scénarios dans lesquels l'écouteur d'événements doit être ajouté plusieurs fois, ce qui n'a pas besoin d'être illustré pour que mon point soit valide.
TetraDev

Je ne suis pas un expert et je n'ai jamais entendu parler de «fermeture» en programmation auparavant, donc je ne comprends pas ce que vous entendez par là.
user3093134

1
Merci d'avoir vérifié, je vais certainement visiter les liens que vous avez publiés aussi. Aussi, je dois dire que vous êtes très poli et honnête monsieur, continuez comme ça et
passez

0

thisà l'intérieur se doThingstrouve l'objet window. Essayez plutôt ceci:

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

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

    return {
        eventHandler: eventHandler
    };
};

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.