Jak przekazać argumenty do funkcji detektora addEventListener?


304

Sytuacja jest trochę jak-

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

Problem polega na tym, że wartość parametru someVarnie jest widoczna w funkcji nasłuchiwania addEventListener, gdzie prawdopodobnie jest ona traktowana jako nowa zmienna.


8
Bardzo jasny artykuł na ten temat: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita

Nie najczystszy sposób, ale działa. Zauważ, że jeśli someVar może być tylko cyfrą lub tekstem: eval ('someObj.addEventListener („click”, function () {some_function (' + someVar + ');});');
Ignas2526

Właśnie miałem ten problem dzisiaj - podane tutaj rozwiązanie jest poprawne (inne rozwiązania mają problemy takie jak problem z pętlą itp.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Odpowiedzi:


206

Napisany kod nie ma absolutnie nic złego. Zarówno some_functioni someVarpowinny być dostępne, na wypadek, gdyby były dostępne w kontekście, w którym anonimowy

function() { some_function(someVar); } 

powstał.

Sprawdź, czy alert daje poszukiwaną wartość, upewnij się, że będzie on dostępny w zakresie funkcji anonimowej (chyba że masz więcej kodu, który działa na tej samej someVarzmiennej obok wywołania do addEventListener)

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

86
To nie działa dla pętli. Zawsze otrzymuję najnowszą wartość, a nie tę, która należała do tej iteracji. Jakieś rozwiązanie?
iMatoria,

6
Czy ktoś wie, dlaczego to nie działa w pętli? Jaki jest powód tego zachowania?
Morfidon

3
@Morfidon, ponieważ funkcja ze zmiennymi globalnymi działa jako zamknięcie w javascript, co oznacza, że ​​zapamiętuje środowisko poza ich zasięgiem leksykalnym. Jeśli po prostu utworzysz inną funkcję w tym samym środowisku, będą one odnosić się do tego samego środowiska.
bugwheels94

16
@Morfidon: W pętli wartość someVar nie jest wartością, którą miał, kiedy dodano detektor, ale wartością, którą ma, kiedy detektor jest wykonywany. Kiedy detektor jest wykonywany, pętla już się zakończyła, więc wartość someVar będzie wartością, którą miała, gdy pętla się zakończyła.
www.admiraalit.nl

4
@ iMatoria Właśnie odkryłem, że utworzenie metody bound functionprzy użyciu .bind()metody rozwiąże problem z pętlami developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien

353

Dlaczego nie pobrać argumentów z atrybutu target zdarzenia?

Przykład:

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 jest językiem zorientowanym na prototypy, pamiętaj!


16
To poprawna odpowiedź, ponieważ pozwala nam korzystać po funkcji „removeEventListener”.
user5260143

14
Nie powinno tak być evt.currentTarget.myParam? Jeśli wewnątrz „someInput” znajduje się inny element, evt.targetmoże odnosić się do elementu wewnętrznego. ( jsfiddle.net/qp5zguay/1 )
Herbertusz

To zachowuje this! Użycie tej metody w maszynopisie wymaga jednak, aby element był anylub tworzył podtyp.
Old Badman Gray,

1
Moje zmienne wracają jako niezdefiniowane ... jakieś myśli, jak to naprawić?
nomaam

1
Jeśli addEventListenertak document, evt.target.myParamto nie działało dla mnie. Musiałem evt.currentTarget.myParamzamiast tego użyć .
turrican_34

67

To pytanie jest stare, ale pomyślałem, że zaoferuję alternatywę przy użyciu .bind () ES5 - dla potomności. :)

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

Pamiętaj tylko, że musisz ustawić swoją funkcję nasłuchiwania z pierwszym parametrem jako argumentem, który przekazujesz do bind (twoja inna funkcja), a drugim parametrem jest teraz zdarzenie (zamiast pierwszego, jak by to było) .


1
Function.prototype.bind () jest naprawdę najlepszym sposobem na rozwiązanie tego problemu. Ponadto działa intuicyjnie wewnątrz pętli - otrzymujesz pożądany zakres leksykalny. Żadnych anonimowych funkcji, IIFE ani specjalnych właściwości przypisywanych do obiektów.
Clint Pachl,

Zobacz zalety i wady IIFE vs bind () .
Clint Pachl,

1
Korzystając z niego Function.prototype.bind(), nie można usunąć detektora zdarzeń , lepiej zamiast tego użyć funkcji curry (patrz odpowiedź @ tomcek112)
pldg 17.09.19

UWAGA: some_other_funcjest zmienną, możesz przekazać dowolną wartość, którą chcesz some_func.
hitautodestruct

28

Możesz po prostu powiązać wszystkie niezbędne argumenty za pomocą polecenia „bind”:

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

W ten sposób zawsze będziesz dostać event, arg1i inne rzeczy przekazanej myPrettyHandler.

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


Dzięki! Próbowałem już, .bind()ale bez wartości zerowej jako pierwszego parametru. co nie działało.
Larphoid,

nie ma potrzeby null, działa dobrze z .bind(event, arg1), przynajmniej z VueJS.
DevonDahon

27

Pytanie dość stare, ale dzisiaj miałem ten sam problem. Najczystszym rozwiązaniem, jakie znalazłem, jest użycie koncepcji curry.

Kod do tego:

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

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

Nadając nazwę funkcji curry, pozwala ona wywołać Object.removeEventListener w celu wyrejestrowania eventListener w późniejszym czasie wykonywania.


4
Cieszę się, że spotkałem się z tą odpowiedzią na temat funkcji curry. Jak jednak usunąć detektor zdarzeń?
Bob

3
Wspaniale jest widzieć dobrą terminologię. Powinieneś być w stanie usunąć detektor zdarzeń, nazywając funkcję curry. zaproponuję edycję.
Matthew Brent

Ta odpowiedź zarejestruje funkcję tyle razy, ile wywoływana jest funkcja addEventListener, ponieważ funkcja some_function (var) zwraca za każdym razem nowo utworzoną funkcję.
Yahia,

nie podoba mi się pomysł nazywania funkcji curry w celu usunięcia słuchacza, ponieważ wtedy masz do czynienia z 2 różnymi nazwami nazw, które musisz śledzić
oldboy

19

Możesz dodawać i usuwać zdarzenia z argumentami, deklarując funkcję jako zmienną.

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

newSrcjest metodą z myaudio, ponieważ parametr funcNamejest zmienną nazwy funkcji

Możesz usunąć słuchacza za pomocą myaudio.removeEventListener('ended',func,false);


12

Możesz przekazać wartość według wartości (nie przez odniesienie) za pomocą funkcji javascript znanej jako zamknięcie :

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

Lub możesz napisać wspólną funkcję zawijania, taką jak 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'

Oto wrapEventCallback(func,var1,var2)jak:

func.bind(null, var1,var2)

1
Wielkie dzięki za tę odpowiedź! OP nie szukał tego, ale myślę, że ludzie, którzy wpisują „Jak przekazać argumenty do addEventListener” w google, będą szukać twojej odpowiedzi. Potrzebuje tylko trochę więcej wyjaśnień :) Edytuję to.
Sindarus,

9

someVarwartość powinna być dostępna tylko w some_function()kontekście, a nie od odbiornika. Jeśli chcesz mieć to w słuchaczu, musisz zrobić coś takiego:

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

I użyć newVar zamiast tego.

Innym sposobem jest zwrócenie someVarwartości some_function()za używanie jej dalej w detektorze (jako nowej zmiennej lokalnej):

var someVar = some_function(someVar);

9

Oto jeszcze inny sposób (ten działa wewnątrz pętli):

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

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

false);

2
To najlepszy sposób. Brzydkie, ale skuteczne wewnątrz pętli, ponieważ wysłanie argumentu do anonimowej funkcji spowoduje przechwycenie var.
Bob

9

Function.prototype.bind () to sposób na powiązanie funkcji celu z określonym zakresem i opcjonalne zdefiniowanie thisobiektu w funkcji celu.

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

Lub uchwycić część zakresu leksykalnego, na przykład w pętli:

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

Wreszcie, jeśli thisparametr nie jest potrzebny w ramach funkcji docelowej:

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

7

Posługiwać się

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

A jeśli chcesz przekazać dowolną niestandardową wartość do tej anonimowej funkcji, najłatwiej to zrobić

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

Działa idealnie w moim projekcie. Mam nadzieję, że to pomoże


Tak, zdecydowanie pomogło, ponieważ utrzymało również oczekiwany zakres w forpętli.
j4v1

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

W ten sposób poprawnie zdałem wydarzenie.


Doskonale, właśnie tak prawie doszedłem do wniosku - właśnie błędnie przekazałem dodatkowe zdarzenie w powiązaniu, gdy go nie ma (jak w kanciastym), co automatycznie przychodzi w tym przypadku.
Manohar Reddy Poreddy

3

Wysyłanie argumentów do funkcji zwrotnej eventListener wymaga utworzenia funkcji izolowanej i przekazania argumentów do tej funkcji izolowanej.

Oto fajna funkcja pomocnika, której możesz użyć. Na podstawie powyższego przykładu „hello world”).

Jedną rzeczą, która jest również potrzebna, jest utrzymanie odniesienia do funkcji, abyśmy mogli czysto usunąć słuchacza.

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

Ta odpowiedź pochodzi z '15, ale właśnie tego potrzebowałem, aby poradzić sobie z tym problemem przy użyciu haka useRef. Jeśli używasz haka referencyjnego i potrzebujesz do niego detektora, który możesz wyczyścić po odmontowaniu komponentu, to jest to. Czwarty argument storeFuncpowinien być Twoją zmienną ref. Wykorzystaj usuwanie swojego słuchacza w taki sposób, aby zacząć:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Jednym ze sposobów jest robienie tego za pomocą funkcji zewnętrznej :

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

Ta metoda zawijania anonimowej funkcji w nawiasy i natychmiastowe jej wywoływanie nazywa się IIFE (wyrażenie funkcji natychmiast wywołane )

Możesz sprawdzić przykład z dwoma parametrami w 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

Jeśli się nie mylę przy użyciu wywołania funkcji z bindfaktycznie tworzy nową funkcję, która jest zwracana przez bindmetodę. Spowoduje to problemy później lub jeśli chcesz usunąć detektor zdarzeń, ponieważ jest to w zasadzie funkcja anonimowa:

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

Więc weź to pod uwagę.

Jeśli używasz ES6, możesz zrobić to samo, co sugerowane, ale nieco czystsze:

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

3

fajna alternatywa dla jednej linii

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

 //some action...

}

2

Spróbuj także tych (IE8 + Chrome. Nie wiem dla 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

Mam nadzieję, że nie ma literówek :-)


Jak trudno byłoby udzielić pełnej odpowiedzi? Czy powinienem to przetestować na FF? Nie zawracam sobie głowy ...
StefanNch

2

Utknąłem w tym, ponieważ używałem go w pętli do wyszukiwania elementów i dodawania do niego listy. Jeśli używasz go w pętli, to będzie działać idealnie

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

W 2019 r. Wiele zmian interfejsu API, najlepsza odpowiedź już nie działa, bez naprawienia błędu.

udostępnij trochę działającego kodu.

Inspirowany wszystkimi powyższymi odpowiedziami.

 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

We wszystkich funkcjach znajduje się specjalna zmienna: argumenty . Możesz przekazać parametry jako parametry anonimowe i uzyskać do nich dostęp (według kolejności) za pomocą argumentów zmienną .

Przykład:

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

Hmm ... Jaki jest powód głosowania? Jeśli nie było to, czego szukasz, wyjaśnij bardziej dokładnie, co masz na myśli (wiem, że odpowiedź na to pytanie została już udzielona). Ale czy mój kod nie odpowiada na to, o co prosiłeś? Specjalna zmienna „argumenty” daje dostęp do wszystkich parametrów wewnątrz funkcji.
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

Poniższe podejście działało dla mnie dobrze. Zmodyfikowano stąd .

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

Poniższa odpowiedź jest poprawna, ale poniższy kod nie działa w IE8, jeśli załóżmy, że skompresowałeś plik js za pomocą yuicompressor. (W rzeczywistości nadal większość ludzi w USA używa IE8)

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

Tak więc możemy rozwiązać powyższy problem w następujący sposób i działa dobrze we wszystkich przeglądarkach

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

Mam nadzieję, że byłby użyteczny dla kogoś, kto kompresuje plik js w środowisku produkcyjnym.

Powodzenia!!


0

Poniższy kod działał dla mnie dobrze (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);
}

Wynik:

0
1
2

Co to jest na świecie?
NiCk Newman

0

Potrzebujesz:

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

0

Prawdopodobnie nie jest optymalny, ale wystarczająco prosty dla tych, którzy nie znają się na superjs. Umieść funkcję wywołującą addEventListener we własnej funkcji. W ten sposób wszelkie przekazane do niego wartości funkcji zachowują swój własny zakres i możesz iterować tę funkcję tyle, ile chcesz.

Przykład Pracowałem z odczytem pliku, ponieważ potrzebowałem przechwycić i wyświetlić podgląd obrazu i nazwy pliku. Zajęło mi trochę czasu, aby uniknąć problemów asynchronicznych podczas korzystania z typu przesyłania wielu plików. Przypadkowo zobaczyłem tę samą „nazwę” na wszystkich rendererach pomimo przesyłania różnych plików.

Pierwotnie cała funkcja readFile () znajdowała się w funkcji readFiles (). Spowodowało to problemy z asynchronicznym zasięgiem.

    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

chciałbym tylko dodać. jeśli ktoś dodaje funkcję, która aktualizuje pola wyboru do detektora zdarzeń, należy użyć event.targetzamiast thiszaktualizować pola wyboru.


0

Mam bardzo uproszczone podejście. To może działać dla innych, ponieważ pomogło mi. To jest ... Jeśli masz wiele elementów / zmiennych przypisanych do tej samej funkcji i chcesz przekazać odwołanie, najprostszym rozwiązaniem jest ...

function Name()
{

this.methodName = "Value"

}

Otóż ​​to. To zadziałało dla mnie. Tak prosty.


-1

Inna alternatywa, być może nie tak elegancka jak użycie bind, ale jest ważna dla zdarzeń w pętli

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

Został przetestowany pod kątem rozszerzeń Google Chrome i być może e.srcElement musi zostać zastąpiony przez e.source w innych przeglądarkach

Znalazłem to rozwiązanie, używając komentarza opublikowanego przez Imatorię, ale nie mogę oznaczyć go jako przydatny, ponieważ nie mam wystarczającej reputacji: D


-1

To rozwiązanie może się przydać

var some_other_function = someVar => function() {
}

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

lub wiążące wartości będą również dobre

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.