Wie übergebe ich Argumente an die Listener-Funktion addEventListener?

304

Die Situation ist etwas wie-

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

Das Problem ist, dass der Wert von someVarin der Listener-Funktion von nicht sichtbar ist addEventListener, wo er wahrscheinlich als neue Variable behandelt wird.

Abhishek Yadav
quelle
8
Ein sehr klarer Artikel zu diesem Thema: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita
Nicht der sauberste Weg, aber macht den Job. Beachten Sie, dass wenn someVar nur eine Ziffer oder ein Text sein kann: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Ignas2526
Hatte gerade dieses Problem heute - die hier angegebene Lösung ist korrekt (andere Lösungen haben Probleme wie Schleifenprobleme usw.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Antworten:

207

An dem von Ihnen geschriebenen Code ist absolut nichts auszusetzen. Beidesome_function und someVarsollten zugänglich sein, falls sie in dem anonymen Kontext verfügbar waren

function() { some_function(someVar); } 

wurde erstellt.

Überprüfen Sie, ob die Warnung den gesuchten Wert enthält. Stellen Sie sicher, dass im Bereich der anonymen Funktion darauf zugegriffen werden kann (es sei denn, Sie haben mehr Code, der mit derselben someVarVariablen neben dem Aufruf von arbeitet addEventListener).

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);
Sergey Ilinsky
quelle
86
Dies funktioniert nicht in der for-Schleife. Ich erhalte immer den neuesten Wert und nicht den, der zu dieser Iteration gehört. Irgendeine Lösung?
iMatoria
6
Weiß jemand, warum es nicht in Schleife funktioniert? Was ist der Grund für dieses Verhalten?
Morfidon
3
@Morfidon, da die Funktion mit globalen Variablen als Verschlüsse in Javascript fungiert, was bedeutet, dass sie sich an die Umgebung außerhalb ihres lexikalischen Bereichs erinnern. Wenn Sie nur unterschiedliche Funktionen in derselben Umgebung erstellen, verweisen diese auf dieselbe Umgebung.
Bugwheels94
16
@Morfidon: In einer Schleife ist der Wert von someVar nicht der Wert, den er hatte, als der Listener hinzugefügt wurde, sondern der Wert, den er hatte, als der Listener ausgeführt wurde. Wenn der Listener ausgeführt wird, ist die Schleife bereits beendet, sodass der Wert von someVar der Wert ist, den er zum Ende der Schleife hatte.
www.admiraalit.nl
4
@iMatoria Ich habe gerade entdeckt, dass das Erstellen einer bound functionmit der .bind()Methode das Problem mit Schleifen entwickler.movilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien
353

Warum nicht einfach die Argumente aus dem Zielattribut des Ereignisses abrufen?

Beispiel:

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 ist eine prototyporientierte Sprache.

Zaloz
quelle
16
Dies ist die richtige Antwort, da wir sie nach der Funktion 'removeEventListener' verwenden können.
user5260143
14
Sollte es nicht sein evt.currentTarget.myParam? Wenn sich in 'someInput' ein anderes Element befindet, evt.targetkann sich das auf das innere Element beziehen. ( jsfiddle.net/qp5zguay/1 )
Herbertusz
Das bewahrt this! Die Verwendung dieser Methode in Typoskript erfordert jedoch, dass das Element ein Untertyp ist anyoder erstellt wird.
Old Badman Grey
1
Meine Variablen kommen immer wieder als undefiniert zurück ... irgendwelche Gedanken darüber, wie man das behebt?
Nomaam
1
Wenn addEventListenerist für document, evt.target.myParamhat bei mir nicht funktioniert. Ich musste evt.currentTarget.myParamstattdessen verwenden.
Turrican_34
67

Diese Frage ist alt, aber ich dachte, ich würde eine Alternative mit .bind () von ES5 anbieten - für die Nachwelt. :) :)

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

Beachten Sie jedoch, dass Sie Ihre Listener-Funktion mit dem ersten Parameter als Argument einrichten müssen, das Sie an bind übergeben (Ihre andere Funktion), und dass der zweite Parameter jetzt das Ereignis ist (anstelle des ersten, wie es gewesen wäre). .

Brian Moore
quelle
1
Function.prototype.bind () ist wirklich der beste Weg, um dieses Problem zu lösen. Außerdem funktioniert es intuitiv in Schleifen - Sie erhalten den gewünschten lexikalischen Bereich. Keine anonymen Funktionen, IIFEs oder speziellen Eigenschaften von Objekten.
Clint Pachl
Sehen Sie die Vor- und Nachteile von IIFE vs bind () .
Clint Pachl
1
Wenn Function.prototype.bind()Sie verwenden, können Sie den Ereignis-Listener nicht entfernen. Verwenden Sie stattdessen besser eine Curry-Funktion (siehe @ tomcek112-Antwort)
pldg
HINWEIS: Ist some_other_funceine Variable, können Sie einen beliebigen Wert übergeben some_func.
Hitautodestruct
28

Sie können einfach alle erforderlichen Argumente mit 'bind' binden:

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

Auf diese Weise bringen Sie immer das event, arg1und andere Sachen übergebenmyPrettyHandler .

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

Oleksandr Tkalenko
quelle
Vielen Dank! Hatte schon versucht, .bind()aber ohne die Null als ersten Parameter. was nicht funktioniert hat.
Larphoid
Keine Notwendigkeit null, es funktioniert gut mit .bind(event, arg1), zumindest mit VueJS.
DevonDahon
27

Ziemlich alte Frage, aber ich hatte heute das gleiche Problem. Die sauberste Lösung, die ich gefunden habe, ist das Curry- Konzept .

Der Code dafür:

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

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

Durch Benennen der Curry-Funktion können Sie Object.removeEventListener aufrufen, um die Registrierung des eventListener zu einem späteren Zeitpunkt aufzuheben.

tomcek112
quelle
4
Ich bin froh, diese Antwort zu finden, in der die Curry-Funktion erwähnt wird. Wie würden Sie den Ereignis-Listener entfernen?
Bob
3
Es ist großartig, eine gute Terminologie zu sehen. Sie sollten in der Lage sein, den Ereignis-Listener zu entfernen, indem Sie die Curry-Funktion benennen. Ich werde eine Bearbeitung vorschlagen.
Matthew Brent
Diese Antwort registriert die Funktion so oft, wie addEventListener aufgerufen wird, da some_function (var) jedes Mal eine neu erstellte Funktion zurückgibt.
Yahia
Ich mag die Idee nicht, die Curry-Funktion benennen zu müssen, um den Listener zu entfernen, weil es sich dann um 2 verschiedene Namespaces handelt, die man im
Auge
19

Sie können Ereignislistener mit Argumenten hinzufügen und entfernen, indem Sie eine Funktion als Variable deklarieren.

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

newSrcist die Methode mit myaudio als Parameter funcNameist die Variable des Funktionsnamens

Sie können den Listener mit entfernen myaudio.removeEventListener('ended',func,false);

Ganesh Krishnan
quelle
12

Sie können somevar als Wert (nicht als Referenz) über eine Javascript-Funktion übergeben, die als Closure bezeichnet wird :

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

Oder Sie schreiben eine allgemeine Wrap-Funktion wie 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'

Hier wrapEventCallback(func,var1,var2)ist wie:

func.bind(null, var1,var2)
ahuigo
quelle
1
Vielen Dank für diese Antwort! Das OP hat nicht danach gesucht, aber ich denke, dass Leute, die "Wie man Argumente an addEventListener übergibt" in Google eingeben, nach Ihrer Antwort suchen werden. Es braucht nur ein bisschen mehr Erklärung :) Ich bearbeite es.
Sindarus
9

someVarWert sollte nur im some_function()Kontext zugänglich sein , nicht vom Hörer. Wenn Sie es im Hörer haben möchten, müssen Sie Folgendes tun:

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

und verwenden newVar stattdessen verwenden.

Die andere Möglichkeit besteht darin, den someVarWert von zurückzugeben, some_function()um ihn im Listener weiter zu verwenden (als neue lokale Variable):

var someVar = some_function(someVar);
Thevs
quelle
9

Hier ist noch ein anderer Weg (Dieser funktioniert innen für Schleifen):

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

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

false);
Hallo Welt
quelle
2
Dies ist der beste Weg. Hässlich, aber effektiv innerhalb von Schleifen, da durch Senden eines Arguments an eine anonyme Funktion die Variable erfasst wird.
Bob
9

Mit Function.prototype.bind () können Sie eine Zielfunktion an einen bestimmten Bereich binden und optional das thisObjekt innerhalb der Zielfunktion definieren .

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

Oder um einen Teil des lexikalischen Bereichs zu erfassen, beispielsweise in einer Schleife:

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

Wenn der thisParameter innerhalb der Zielfunktion nicht benötigt wird:

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

Verwenden

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

Wenn Sie einen benutzerdefinierten Wert an diese anonyme Funktion übergeben möchten, ist dies am einfachsten

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

Funktioniert perfekt in meinem Projekt. Hoffe das wird helfen

kta
quelle
Ja, auf jeden Fall geholfen, da es auch den erwarteten Umfang innerhalb einer forSchleife beibehielt .
j4v1
4
    $form.addEventListener('submit', save.bind(null, data, keyword, $name.value, myStemComment));
    function save(data, keyword, name, comment, event) {

So habe ich das Ereignis richtig bestanden.

chovy
quelle
Ausgezeichnet, so kam ich fast zu dem Schluss - nur fälschlicherweise wurde ein zusätzliches Ereignis in bind falsch übergeben, wenn es nicht vorhanden ist (wie in eckig), was in diesem Fall automatisch eintritt.
Manohar Reddy Poreddy
3

Das Senden von Argumenten an die Rückruffunktion eines eventListener erfordert das Erstellen einer isolierten Funktion und das Übergeben von Argumenten an diese isolierte Funktion.

Hier ist eine nette kleine Hilfsfunktion, die Sie verwenden können. Basierend auf dem obigen Beispiel "Hallo Welt".)

Eine Sache, die auch benötigt wird, ist, einen Verweis auf die Funktion beizubehalten, damit wir den Listener sauber entfernen können.

// 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);
Bob
quelle
Diese Antwort stammt aus dem Jahr '15, aber es ist genau das, was ich brauchte, um dieses Problem mit einem useRef-Hook zu lösen. Wenn Sie einen Ref-Hook verwenden und dafür einen Listener benötigen, mit dem Sie das Aufheben der Bereitstellung von Komponenten bereinigen können, ist dies genau das Richtige. Das 4. Argument storeFuncsollte Ihre Referenzvariable sein. Setzen Sie Ihre Listener-Entfernung in einen useEffect wie diesen und Sie können loslegen:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B
3

Eine Möglichkeit besteht darin, dies mit einer äußeren Funktion zu tun :

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

Diese Methode, eine anonyme Funktion in Klammern zu setzen und sofort aufzurufen, wird als IIFE (Sofort aufgerufener Funktionsausdruck) bezeichnet.

Sie können ein Beispiel mit zwei Parametern unter http://codepen.io/froucher/pen/BoWwgz überprüfen .

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

Wenn ich mich nicht irre, wenn ich die Funktion mit bindaufrufe, wird tatsächlich eine neue Funktion erstellt, die von der bindMethode zurückgegeben wird. Dies führt später zu Problemen oder wenn Sie den Ereignis-Listener entfernen möchten, da dies im Grunde wie eine anonyme Funktion ist:

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

Denken Sie also daran.

Wenn Sie ES6 verwenden, können Sie das Gleiche wie vorgeschlagen tun, jedoch etwas sauberer:

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

schöne einzeilige Alternative

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

 //some action...

}
Gon82
quelle
2

Versuchen Sie auch diese (IE8 + Chrome. Ich weiß nicht für 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

Hoffe es gibt keine Tippfehler :-)

DMike92
quelle
Wie schwer wäre es, eine vollständige Antwort zu geben? Soll ich das auf FF testen? Nun, ich werde mich nicht darum
kümmern
2

Ich steckte darin fest, als ich es in einer Schleife verwendete, um Elemente zu finden und Listener hinzuzufügen. Wenn Sie es in einer Schleife verwenden, funktioniert dies einwandfrei

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
quelle
2

Im Jahr 2019 viele API-Änderungen, die beste Antwort funktioniert nicht mehr, ohne Fehler zu beheben.

Teilen Sie einen Arbeitscode.

Inspiriert von allen obigen Antworten.

 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
quelle
1

In allen Funktionen gibt es eine spezielle Variable: Argumente . Sie können Ihre Parameter als anonyme Parameter übergeben und über die Argumente (nach Reihenfolge) darauf zugreifen .

Beispiel:

var someVar = some_other_function();
someObj.addEventListener("click", function(someVar){
    some_function(arguments[0]);
}, false);
StanE
quelle
Hmm ... Was ist der Grund für die Ablehnung? Wenn es nicht das war, wonach Sie gesucht haben, erklären Sie bitte klarer, was Sie meinen (ich weiß, dass die Frage bereits beantwortet wurde). Aber antwortet mein Code nicht auf das, wonach Sie gefragt haben? Mit der speziellen Variablen "Argumente" haben Sie Zugriff auf alle Parameter innerhalb einer Funktion.
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
quelle
1

Der folgende Ansatz hat bei mir gut funktioniert. Von hier geändert .

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
quelle
0

Die folgende Antwort ist richtig, aber der folgende Code funktioniert in IE8 nicht, wenn Sie die js-Datei mit yuicompressor komprimiert haben. (Tatsächlich verwenden immer noch die meisten US-Völker IE8)

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

Daher können wir das oben genannte Problem wie folgt beheben und es funktioniert in allen Browsern einwandfrei

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

Hoffe, es wäre nützlich für jemanden, der die js-Datei in der Produktionsumgebung komprimiert.

Viel Glück!!

Asik
quelle
0

Der folgende Code hat bei mir gut funktioniert (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);
}

Ausgabe:

0
1
2
Šerg
quelle
Was in aller Welt ist das?
NiCk Newman
0

Du brauchst:

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

Wahrscheinlich nicht optimal, aber einfach genug für diejenigen, die nicht besonders versiert sind. Fügen Sie die Funktion, die addEventListener aufruft, in eine eigene Funktion ein. Auf diese Weise behalten alle übergebenen Funktionswerte ihren eigenen Bereich bei, und Sie können diese Funktion beliebig oft durchlaufen.

Beispiel Ich habe mit dem Lesen von Dateien gearbeitet, da ich eine Vorschau des Bildes und des Dateinamens erfassen und rendern musste. Ich habe eine Weile gebraucht, um asynchrone Probleme bei der Verwendung eines Upload-Typs mit mehreren Dateien zu vermeiden. Ich würde versehentlich auf allen Renderings den gleichen 'Namen' sehen, obwohl ich verschiedene Dateien hochgeladen habe.

Ursprünglich befand sich die gesamte Funktion readFile () in der Funktion readFiles (). Dies verursachte Probleme mit dem asynchronen Gültigkeitsbereich.

    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
quelle
0

möchte nur hinzufügen. Wenn jemand eine Funktion hinzufügt, die Kontrollkästchen für einen Ereignis-Listener aktualisiert, müssen Sie diese verwenden, event.targetanstatt thisdie Kontrollkästchen zu aktualisieren.

Bruce Tong
quelle
0

Ich habe einen sehr simplen Ansatz. Dies kann für andere funktionieren, da es mir geholfen hat. Es ist ... Wenn Sie mehreren Elementen / Variablen dieselbe Funktion zugewiesen haben und die Referenz übergeben möchten, ist die einfachste Lösung ...

function Name()
{

this.methodName = "Value"

}

Das ist es. Es hat bei mir funktioniert. So einfach.

AAYUSH SHAH
quelle
-1

Eine andere Alternative, die vielleicht nicht so elegant ist wie die Verwendung von bind, aber für Ereignisse in einer Schleife gültig ist

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

Es wurde auf Google Chrome-Erweiterungen getestet und möglicherweise muss e.srcElement in anderen Browsern durch e.source ersetzt werden

Ich habe diese Lösung anhand des von Imatoria geposteten Kommentars gefunden , kann sie jedoch nicht als nützlich markieren, da ich nicht genügend Ruf habe: D.

fhuertas
quelle
-1

Diese Lösung kann gut aussehen

var some_other_function = someVar => function() {
}

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

oder Wertsachen binden wird auch gut sein

Kleeblätter
quelle