AddEventListener dinleyici işlevine bağımsız değişkenler nasıl iletilir?


304

Durum bir şekilde ...

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

Sorun, değerinin , muhtemelen yeni bir değişken olarak ele someVaralındığı dinleyici işlevi içinde görünür olmamasıdır addEventListener.


8
Konu hakkında çok net bir makale: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita

En temiz yol değil, ama işi yapar. SomeVar öğesinin yalnızca rakam veya metin olabileceğini unutmayın: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Mayıs 14:37

Bugün bu sorunu
yaşadım

Yanıtlar:


207

Yazdığınız kodda kesinlikle yanlış bir şey yok. Hem some_functionve someVarerişilebilir olmalıdır, böyle bir durumda anonim yerde, bağlam içinde kullanılabilen

function() { some_function(someVar); } 

yaratıldı.

Uyarının size aradığınız değeri verip vermediğini kontrol edin, anonim işlev kapsamında erişilebilir olduğundan emin olun ( someVarçağrının yanındaki aynı değişken üzerinde çalışan daha fazla kodunuz yoksa addEventListener)

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

86
Bu döngü için işe yaramaz. Bu iterasyona ait olanı değil, daima en son değeri alırım. Herhangi bir çözüm?
iMatoria

6
Neden döngüde çalışmadığını bilen var mı? Bu davranışın nedeni nedir?
Morfidon

3
@Morfidon küresel değişkenlerle fonksiyonu olarak hareket çünkü kapanışları onların sözcük kapsamı dışında çevreyi hatırlar anlamına javascript. Aynı ortamda farklı işlevler oluşturursanız, aynı ortama başvururlar.
bugwheels94

16
@Morfidon: Bir döngüde, someVar'ın değeri dinleyici eklendiğinde sahip olduğu değer değil, dinleyici yürütüldüğünde sahip olduğu değerdir. Dinleyici yürütüldüğünde, döngü zaten sona ermiştir, bu nedenle someVar'ın değeri döngü sona erdiğinde sahip olduğu değer olacaktır.
www.admiraalit.nl

4
@ iMatoria Bir yöntem bound functionkullanarak oluşturmanın .bind()sorunu döngülerde çözeceğini keşfettim developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien

353

Neden sadece olayın hedef özelliğinden argüman almıyorsunuz?

Misal:

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 prototip odaklı bir dildir, unutmayın!


16
Bu, 'removeEventListener' işlevinden sonra kullanmamıza izin verdiği için doğru cevaptır.
user5260143

14
Olmamalı mı evt.currentTarget.myParam? 'SomeInput' içinde başka bir öğe varsa evt.target, iç öğeye atıfta bulunabilir. ( jsfiddle.net/qp5zguay/1 )
Herbertusz

Bu korur this! Bu yazı anytipinin typescript içinde kullanılması, elemanın olmasını ya da bir alt tip yapmasını gerektirir .
Old Badman Gray

1
Değişkenlerim tanımsız olarak geri gelmeye devam ediyor ... bunu nasıl düzeltebileceğine dair bir fikrin var mı?
nomaam

1
Eğer addEventListeneriçindir document, evt.target.myParambenim için çalışmadı. Onun evt.currentTarget.myParamyerine kullanmak zorunda kaldım .
turrican_34

67

Bu soru eski ama gelecek nesiller için ES5'in .bind () yöntemini kullanarak bir alternatif sunacağımı düşündüm. :)

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

Dinleyici işlevinizi, bağladığınız argüman olarak (diğer işleviniz) ilk parametre ile ayarlamanız gerektiğini ve ikinci parametre şimdi olay (ilk olduğu gibi, olduğu gibi) olduğunu unutmayın. .


1
Function.prototype.bind () gerçekten bu sorunu çözmenin en iyi yoludur. Ayrıca, döngüler içinde sezgisel olarak çalışır - istediğiniz sözlüksel kapsamı elde edersiniz. Anonim işlevler, IIFE'ler veya nesnelere takılan özel özellikler yoktur.
Clint Pachl

IIFE vs bind () artı ve eksilerini görün .
Clint Pachl

1
Kullanarak Function.prototype.bind()edemeyeceğini olay dinleyicisi kaldırmak , daha iyi yerine tımar işlevini kullanmak için (@ tomcek112 cevaba bakınız)
pldg

NOT: some_other_funcbir değişkendir, istediğiniz değeri iletebilirsiniz some_func.
hitautodestruct

28

Gerekli tüm argümanları 'bind' ile bağlayabilirsiniz:

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

Bu şekilde her zaman alırsınız event, arg1geçirilen ve başka şeyler myPrettyHandler.

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


Teşekkürler! Zaten denemişti .bind()ama ilk parametre olarak null olmadan. ki bu işe yaramadı.
Larphoid

Gerek hiçbir null, onunla para cezası çalışır .bind(event, arg1)en azından VueJS ile.
DevonDahon

27

Oldukça eski bir soru ama bugün de aynı sorunu yaşadım. Bulduğum en temiz çözüm, köri .

Bunun kodu:

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

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

Curried işlevini adlandırarak, eventListener'ın daha sonraki bir yürütme zamanında kaydını silmek için Object.removeEventListener öğesini çağırmanıza izin verir.


4
Kıvrımlı işlevden söz ederek bu cevapla karşılaştığım için memnunum Etkinlik dinleyicisini nasıl kaldırırsınız?
bob

3
İyi bir terminoloji görmek harika. Olay dinleyicisini curried işlevini adlandırarak kaldırabilirsiniz. bir düzenleme teklif edeceğim.
Matthew Brent

Bu cevap işlevi addEventListener çağrıldığı kadar çok kez kaydeder, çünkü some_function (var) her zaman yeni oluşturulan bir işlev döndürür.
Yahia

dinleyiciyi kaldırmak için curried işlevi adlandırmak zorunda fikrini sevmiyorum çünkü daha sonra u izlemeniz gereken 2 fark ad alanı ile uğraşmak
oldboy

19

Bir işlevi değişken olarak bildirerek bağımsız değişkenli olay dinleyicileri ekleyebilir ve kaldırabilirsiniz.

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

newSrcparametre funcNameişlev adı değişkeni olduğu için myaudio yöntemidir

Dinleyiciyi şununla kaldırabilirsiniz: myaudio.removeEventListener('ended',func,false);


12

Kapatma olarak bilinen bir javascript özelliği aracılığıyla somevar değerini değere göre (referans olarak değil) iletebilirsiniz :

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

Veya aşağıdaki gibi ortak bir sarma işlevi yazabilirsiniz 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'

İşte wrapEventCallback(func,var1,var2)şunun gibi:

func.bind(null, var1,var2)

1
Bu cevap için çok teşekkürler! OP bunu aramıyordu, ancak google'a "addEventListener için argümanlar nasıl aktarılır" yazan kişilerin cevabınızı arayacağını düşünüyorum. Biraz daha açıklamaya ihtiyacı var :) Düzenliyorum.
Sindarus

9

someVardeğerin some_function()dinleyiciden değil, yalnızca bağlam içinde erişilebilir olması gerekir . Dinleyicinin içinde olmasını isterseniz, şöyle bir şey yapmalısınız:

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

ve kullanımı newVaryerine.

Diğer yol, someVardeğeri some_function()dinleyicide daha fazla kullanmak için (yeni bir yerel değişken olarak) döndürmektir :

var someVar = some_function(someVar);

9

İşte başka bir yol (Bu, döngüler için içeride çalışır):

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

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

false);

2
Bu en iyi yol. Çirkin, ama döngüler içinde etkili çünkü anonim bir işleve bir argüman göndererek var yakalar.
bob

9

Function.prototype.bind () , bir hedef işlevi belirli bir kapsama bağlama ve isteğe bağlı thisolarak hedef işlev içindeki nesneyi tanımlama yoludur .

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

Veya sözlü kapsamın bazılarını yakalamak için, örneğin bir döngüde:

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

Son olarak, this parametre hedef işlev içinde gerekli değilse:

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

7

kullanım

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

Ve herhangi bir özel değeri bu anonim işleve geçirmek istiyorsanız, bunu yapmanın en kolay yolu

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

Projemde mükemmel çalışıyor. Umarım bu yardımcı olur


Evet, kesinlikle bir fordöngü içinde beklenen kapsamı koruduğundan kesinlikle yardımcı oldu .
j4v1

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

Bu şekilde olayı düzgün bir şekilde geçirdim.


Mükemmel, bu şekilde neredeyse sonuçlandırıyordum - orada olmadığında (açısal olarak olduğu gibi) bu durumda otomatik olarak gelen, yanlış bir şekilde bağlanan ekstra olayı geçti.
Manohar Reddy Poreddy

3

Bir eventListener öğesinin geri çağırma işlevine bağımsız değişkenler göndermek için yalıtılmış bir işlev oluşturulması ve bağımsız değişkenlerin bu yalıtılmış işleve iletilmesi gerekir.

İşte kullanabileceğiniz güzel bir küçük yardımcı işlevi. Yukarıdaki "merhaba dünya" örneğine dayanarak.)

Ayrıca ihtiyaç duyulan bir şey, dinleyiciyi temiz bir şekilde kaldırabilmemiz için işleve bir referans sağlamaktır.

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

Bu cevap '15'ten geliyor ama bir useRef kancası kullanarak bu sorunu ele almak için tam olarak ihtiyacım olan şey. Bir ref çengelini kullanıyorsanız ve bunun için parça sökme işlemini temizleyebileceğiniz bir dinleyiciye ihtiyacınız varsa, işte budur. 4. arg storeFunc, ref değişkeniniz olmalıdır. Dinleyicinizin kaldırılmasını bir kullanımda bırakınEtkisini izleyin ve gitmekte fayda var:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Bir yolu bunu bir dış işlevle yapmaktır :

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

Anonim bir işlevi parantez içine almanın ve hemen çağırmanın bu yöntemine IIFE denir (Hemen İşlev İfadesi)

Http://codepen.io/froucher/pen/BoWwgz adresinde iki parametreli bir örneği kontrol edebilirsiniz .

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

3

Fonksiyonu çağırarak yanlış kullanmıyorsam, bindaslında bindyöntem tarafından döndürülen yeni bir fonksiyon yaratır . Bu, daha sonra sorunlara neden olur veya temel olarak anonim bir işlev gibi olduğu için olay dinleyicisini kaldırmak isterseniz:

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

Bunu aklınızda bulundurun.

ES6 kullanıyorsanız, önerilenle aynı şeyi yapabilir, ancak biraz daha temizleyebilirsiniz:

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

3

güzel bir satır alternatif

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

 //some action...

}

2

Ayrıca bu deneyin (IE8 + Chrome. FF için bilmiyorum):

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

Yazım hatası yok umut :-)


Tam bir cevap vermek ne kadar zor olurdu? Bunu FF'de test etmeli miyim?
Rahatsız

2

Ben öğeleri bulmak ve ona listner eklemek için bir döngü içinde kullandığım gibi bu sıkışmış. Bir döngüde kullanıyorsanız, bu mükemmel bir şekilde çalışacaktır

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

2019'da birçok api değişikliği, en iyi cevap artık düzeltme hatası olmadan çalışmıyor.

bazı çalışma kodlarını paylaşın.

Yukarıdaki yanıttan ilham aldı.

 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

Tüm fonksiyonların içinde özel bir değişken vardır: argümanlar . Parametrelerinizi anonim parametreler olarak iletebilir ve bağımsız değişkenler aracılığıyla bunlara (sırayla) erişebilirsiniz değişkeni .

Misal:

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

Hmm ... Downvote'un nedeni nedir? Eğer aradığınız yer bu değilse, lütfen ne demek istediğinizi daha açık bir şekilde açıklayın (sorunun zaten yanıtlandığını biliyorum). Ama kodum istediğini cevaplamıyor mu? Özel değişken "arguments", bir fonksiyon içindeki tüm parametrelere erişmenizi sağlar.
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

Aşağıdaki yaklaşım benim için iyi çalıştı. Buradan değiştirildi .

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

Aşağıdaki yanıt doğrudur, ancak yuicompressor kullanarak js dosyasını sıkıştırdığınızı varsayalım aşağıdaki kod IE8'de çalışmıyor. (Aslında, hala ABD halklarının çoğu IE8 kullanıyor)

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

Yani, yukarıdaki sorunu aşağıdaki gibi düzeltebiliriz ve tüm tarayıcılarda iyi çalışır

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

Umarım, js dosyasını üretim ortamında sıkıştıran biri için yararlı olacaktır.

İyi şanslar!!


0

Aşağıdaki kod benim için iyi çalıştı (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);
}

Çıktı:

0
1
2

Bu dünyada ne?
NiCk Newman

0

Gerekenler:

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

0

Muhtemelen optimal değil, ama süper js anlayışlı olmayanlar için yeterince basit. AddEventListener öğesini çağıran işlevi kendi işlevine yerleştirin. Bu şekilde, içine aktarılan herhangi bir işlev değeri kendi kapsamını korur ve bu işlevi istediğiniz kadar tekrarlayabilirsiniz.

Örnek Görüntü ve dosya adının bir önizlemesini yakalamak ve oluşturmak için ihtiyaç duyduğum için dosya okuma ile çalıştım. Birden fazla dosya yükleme türü kullanırken eşzamansız sorunları önlemek biraz zamanımı aldı. Farklı dosyalar yüklemeye rağmen yanlışlıkla tüm render'larda aynı 'adı' görürdüm.

Başlangıçta, tüm readFile () işlevi readFiles () işlevindeydi. Bu, eşzamansız kapsam belirleme sorunlarına neden oldu.

    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

sadece eklemek istiyorum. birisi olay dinleyicisine onay kutularını güncelleyen bir işlev ekliyorsa , onay kutularını güncellemek event.targetyerine kullanmanız gerekir this.


0

Çok basit bir yaklaşımım var. Bu bana yardımcı olduğu için başkaları için de işe yarayabilir. Bu ... Aynı işleve atanmış birden çok öğeye / değişkene sahip olduğunuzda ve referansı geçmek istediğinizde, en basit çözüm ...

function Name()
{

this.methodName = "Value"

}

Bu kadar. Benim için çalıştı. Çok basit.


-1

Diğer alternatif, belki de bağlama kullanımı kadar zarif değil, ancak bir döngüdeki olaylar için geçerlidir

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

Google Chrome uzantıları için test edilmiştir ve belki de e.srcElement, diğer tarayıcılarda e.source ile değiştirilmelidir

Bu çözümü Imatoria tarafından gönderilen yorumu kullanarak buldum, ancak yararlı olarak işaretleyemem çünkü yeterli itibarım yok: D


-1

Bu çözüm bakmak için iyi olabilir

var some_other_function = someVar => function() {
}

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

veya bağlama değerleri de iyi olacak

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.