Come passare argomenti alla funzione listener addEventListener?


304

La situazione è un po 'come-

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

Il problema è che il valore di someVarnon è visibile all'interno della funzione listener di addEventListener, dove è probabilmente trattata come una nuova variabile.


8
Un articolo molto chiaro sulla questione: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita

Non è il modo più pulito, ma fa il lavoro. Nota che se someVar poteva essere solo una cifra o un testo: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Ignas2526,

Appena avuto questo problema oggi - soluzione qui fornite sono corrette (le altre soluzioni hanno problemi come per il rilascio loop, etc.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Risposte:


207

Non c'è assolutamente nulla di sbagliato nel codice che hai scritto. Entrambi some_functione someVardovrebbero essere accessibili, nel caso fossero disponibili nel contesto in cui erano anonimi

function() { some_function(someVar); } 

è stato creato.

Controlla se l'avviso ti dà il valore che stavi cercando, assicurati che sia accessibile nell'ambito della funzione anonima (a meno che tu non abbia più codice che opera sulla stessa someVarvariabile accanto alla chiamata a addEventListener)

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

86
Questo non funziona in loop. Ricevo sempre l'ultimo valore e non quello che apparteneva a quell'iterazione. Qualche soluzione?
iMatoria,

6
Qualcuno sa perché non funziona in loop? Qual è la ragione di quel comportamento?
Morfidon,

3
@Morfidon perché la funzione con variabili globali agisce come chiusure in JavaScript, il che significa che ricorda l'ambiente al di fuori del loro ambito lessicale. Se crei semplicemente una funzione diversa nello stesso ambiente, faranno riferimento allo stesso ambiente.
bugwheels94

16
@Morfidon: in un ciclo, il valore di someVar non è il valore che aveva quando è stato aggiunto il listener, ma il valore che ha quando viene eseguito il listener. Quando viene eseguito l'ascoltatore, il ciclo è già terminato, quindi il valore di someVar sarà il valore che aveva al termine del ciclo.
www.admiraalit.nl

4
@iMatoria Ho appena scoperto che la creazione di un metodo bound functionusando .bind()risolverà il problema con i loop developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien il

354

Perché non ottenere semplicemente gli argomenti dall'attributo target dell'evento?

Esempio:

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 è un linguaggio orientato ai prototipi, ricorda!


16
Questa è la risposta corretta perché ci consente di utilizzare dopo la funzione 'removeEventListener'.
user5260143

14
Non dovrebbe essere evt.currentTarget.myParam? Se all'interno di 'someInput' è presente un altro elemento, è evt.targetpossibile fare riferimento all'elemento interno. ( jsfiddle.net/qp5zguay/1 )
Herbertusz,

Questo conserva this! L'uso di questo metodo in dattiloscritto richiede tuttavia che l'elemento sia anyo che crei un sottotipo.
Old Badman Grey,

1
Le mie variabili continuano a tornare indefinite ... hai pensato a come risolverlo?
nomaam,

1
Se addEventListenerè per document, evt.target.myParamnon ha funzionato per me. Ho dovuto usare evt.currentTarget.myParaminvece.
turrican_34,

67

Questa domanda è vecchia ma ho pensato di offrire un'alternativa usando ESB's .bind () - per i posteri. :)

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

Basta essere consapevoli del fatto che è necessario impostare la funzione listener con il primo parametro come argomento che si sta passando in bind (l'altra funzione) e il secondo parametro è ora l'evento (anziché il primo, come sarebbe stato) .


1
Function.prototype.bind () è davvero il modo migliore per risolvere questo problema. Inoltre, funziona in modo intuitivo all'interno dei loop: ottieni l'ambito lessicale che desideri. Nessuna funzione anonima, IIFE o proprietà speciali attaccate agli oggetti.
Clint Pachl,

Vedi i pro e i contro di IIFE vs bind () .
Clint Pachl,

1
Usando Function.prototype.bind()non puoi rimuovere il listener di eventi , meglio usare invece una funzione di curry (vedi risposta @ tomcek112)
pldg

NOTA: some_other_funcè una variabile, puoi passare qualunque valore tu voglia some_func.
Hitautodestruct

28

Puoi semplicemente associare tutti gli argomenti necessari con 'bind':

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

In questo modo avrete sempre la event, arg1, e altre cose passato a myPrettyHandler.

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


Grazie! Ci avevo già provato .bind()ma senza lo null come primo parametro. che non ha funzionato.
Larphoid,

non c'è bisogno null, funziona benissimo .bind(event, arg1), almeno con VueJS.
DevonDahon,

27

Una domanda abbastanza vecchia, ma oggi ho avuto lo stesso problema. La soluzione più pulita che ho trovato è quella di utilizzare il concetto di curry.

Il codice per questo:

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

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

Assegnando un nome alla funzione curry, è possibile chiamare Object.removeEventListener per annullare la registrazione di eventListener in un momento successivo dell'esecuzione.


4
Sono contento di incontrare questa risposta menzionando la funzione al curry. Come rimuoveresti il ​​listener di eventi?
bob

3
Fantastico vedere una buona terminologia. Dovresti essere in grado di rimuovere il listener di eventi nominando la funzione Curry. proporrò una modifica.
Matthew Brent,

Questa risposta registra la funzione tutte le volte che viene chiamata addEventListener, poiché some_function (var) restituisce ogni volta una funzione appena creata.
Yahia,

non mi piace l'idea di dover nominare la funzione al curry per rimuovere l'ascoltatore perché poi hai a che fare con 2 spazi dei nomi diff di cui devi tenere traccia
oldboy

19

È possibile aggiungere e rimuovere listener di eventi con argomenti dichiarando una funzione come variabile.

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

newSrcè il metodo con myaudio come parametro funcNameè la variabile del nome della funzione

È possibile rimuovere il listener con myaudio.removeEventListener('ended',func,false);


12

È possibile passare somevar per valore (non per riferimento) tramite una funzione javascript nota come chiusura :

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

Oppure potresti scrivere una funzione di avvolgimento comune come 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'

Ecco wrapEventCallback(func,var1,var2)come:

func.bind(null, var1,var2)

1
Grazie mille per questa risposta! L'OP non lo cercava, ma penso che le persone che digitano "How to pass args to addEventListener" su Google cercheranno la tua risposta. Ha solo bisogno di qualche spiegazione in più :) Lo sto modificando.
Sindarus,

9

someVaril valore dovrebbe essere accessibile solo nel some_function()contesto, non da quello dell'ascoltatore. Se ti piace averlo all'interno dell'ascoltatore, devi fare qualcosa del tipo:

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

e usa newVarinvece.

L'altro modo è di restituire someVarvalore some_function()per usarlo ulteriormente nel listener (come una nuova var locale):

var someVar = some_function(someVar);

9

Ecco un altro modo (questo funziona all'interno per i loop):

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

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

false);

2
Questo è il modo migliore Brutto, ma efficace nei cicli poiché inviando un argomento in una funzione anonima catturerà il var.
bob

9

Function.prototype.bind () è il modo per associare una funzione target a un determinato ambito e facoltativamente definire l' thisoggetto all'interno della funzione target.

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

O per acquisire parte dell'ambito lessicale, ad esempio in un ciclo:

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

Infine, se il thisparametro non è necessario all'interno della funzione target:

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

7

Uso

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

E se vuoi passare qualsiasi valore personalizzato a questa funzione anonima, il modo più semplice per farlo è

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

Funziona perfettamente nel mio progetto. Spero che questo possa aiutare


Sì, sicuramente aiutato, poiché ha anche mantenuto l'ambito previsto all'interno di un forciclo.
j4v1

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

È così che l'evento è stato passato correttamente.


Eccellente, questo è il modo in cui stavo quasi concludendo: è stato erroneamente superato un evento extra nel bind quando non c'è (come in angolare), che viene automaticamente in questo caso.
Manohar Reddy Poreddy,

3

L'invio di argomenti alla funzione di callback di un eventListener richiede la creazione di una funzione isolata e il passaggio di argomenti a quella funzione isolata.

Ecco una piccola funzione di aiuto che puoi usare. Basato sull'esempio "ciao mondo" sopra.)

Una cosa che è anche necessaria è mantenere un riferimento alla funzione in modo da poter rimuovere l'ascoltatore in modo pulito.

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

Questa risposta è del '15 ma è esattamente ciò di cui avevo bisogno per gestire questo problema con l'utilizzo di un hook useRef. Se stai usando un hook ref e hai bisogno di un listener per poter ripulire lo smontaggio dei componenti, questo è tutto. Il 4o argomento storeFuncdovrebbe essere la tua variabile ref. Metti in uso la tua rimozione dell'ascoltatore Effetto come questo e sei a posto:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Un modo è farlo con una funzione esterna :

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

Questo metodo per racchiudere una funzione anonima tra parentesi e chiamarla subito si chiama IIFE (Espressione di funzione immediatamente invocata )

È possibile controllare un esempio con due parametri in 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

Se non sbaglio, l'utilizzo della chiamata della funzione con bindcrea effettivamente una nuova funzione che viene restituita dal bindmetodo. Questo ti causerà problemi in seguito o se desideri rimuovere il listener di eventi, in quanto è fondamentalmente come una funzione anonima:

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

Quindi tienilo a mente.

Se stai usando ES6 potresti fare lo stesso suggerito ma un po 'più pulito:

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

3

bella alternativa a una riga

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

 //some action...

}

2

Prova anche questi (IE8 + Chrome. Non so per 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

Spero non ci siano errori di battitura :-)


Quanto sarebbe difficile dare una risposta completa? Devo testarlo su FF? Bene, non mi preoccuperò ...
StefanNch

2

Ero bloccato in questo mentre lo stavo usando in un ciclo per trovare elementi e aggiungere listener ad esso. Se lo stai usando in un ciclo, allora funzionerà perfettamente

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

Nel 2019, molte modifiche api, la risposta migliore non funziona più, senza correzione di bug.

condividere un codice funzionante.

Ispirato da tutta la risposta sopra.

 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

C'è una variabile speciale all'interno di tutte le funzioni: argomenti . Puoi passare i tuoi parametri come parametri anonimi e accedervi (per ordine) attraverso la variabile argomenti .

Esempio:

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

Hmm ... Qual è il motivo del downvote? Se non era quello che cercavi, ti preghiamo di spiegare più chiaramente cosa intendi (so che alla domanda è già stata data una risposta). Ma il mio codice non risponde a quello che hai chiesto? La speciale variabile "argomenti" ti dà accesso a tutti i parametri all'interno di una funzione.
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

Il seguente approccio ha funzionato bene per me. Modificato da qui .

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 seguente risposta è corretta ma il codice seguente non funziona in IE8 se supponiamo di aver compresso il file js usando yuicompressor. (In effetti, ancora la maggior parte dei popoli degli Stati Uniti usa IE8)

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

Quindi, possiamo risolvere il problema sopra come segue e funziona bene in tutti i browser

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

Spero, sarebbe utile per qualcuno che sta comprimendo il file js nell'ambiente di produzione.

In bocca al lupo!!


0

Il seguente codice ha funzionato bene per me (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);
}

Produzione:

0
1
2

Cosa nel mondo è questo?
NiCk Newman,

0

Hai bisogno:

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

0

Probabilmente non ottimale, ma abbastanza semplice per coloro che non sono molto esperti. Inserire la funzione che chiama addEventListener nella propria funzione. In questo modo tutti i valori di funzione passati al suo interno mantengono il loro ambito e puoi iterare su quella funzione quanto vuoi.

Esempio Ho elaborato la lettura dei file in base alle necessità per acquisire e visualizzare in anteprima l'immagine e il nome file. Mi ci è voluto un po 'per evitare problemi asincroni quando si utilizza un tipo di caricamento di più file. Vorrei accidentalmente vedere lo stesso "nome" su tutti i render nonostante il caricamento di file diversi.

Inizialmente, tutta la funzione readFile () era all'interno della funzione readFiles (). Ciò ha causato problemi di scoping asincroni.

    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

vorrei solo aggiungere. se qualcuno sta aggiungendo una funzione che aggiorna le caselle di controllo a un listener di eventi, dovresti usare event.targetinvece di thisaggiornare le caselle di controllo.


0

Ho un approccio molto semplicistico. Questo potrebbe funzionare per gli altri perché mi ha aiutato. È ... Quando si hanno più elementi / variabili assegnati a una stessa funzione e si desidera passare il riferimento, la soluzione più semplice è ...

function Name()
{

this.methodName = "Value"

}

Questo è tutto. Ha funzionato per me. Così semplice.


-1

Altra alternativa, forse non elegante come l'uso di bind, ma è valida per eventi in loop

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

È stato testato per le estensioni di Google Chrome e forse e.srcElement deve essere sostituito da e.source in altri browser

Ho trovato questa soluzione usando il commento pubblicato da Imatoria ma non posso contrassegnarla come utile perché non ho abbastanza reputazione: D


-1

Questa soluzione può essere utile per la ricerca

var some_other_function = someVar => function() {
}

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

o legare oggetti di valore sarà anche buono

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.