Bagaimana cara menyampaikan argumen ke fungsi pendengar addEventListener?

304

Situasinya agak seperti-

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

Masalahnya adalah bahwa nilai someVartidak terlihat di dalam fungsi pendengar addEventListener, di mana ia mungkin diperlakukan sebagai variabel baru.

Abhishek Yadav
sumber
8
Artikel yang sangat jelas tentang masalah ini: toddmotto.com/avoiding-anonymous-javascript-functions
Nobita
Bukan cara terbersih, tetapi melakukan pekerjaan. Perhatikan bahwa jika someVar hanya bisa berupa digit atau teks: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Ignas2526
Baru saja mengalami masalah ini hari ini - solusi yang diberikan di sini benar (solusi lain memiliki masalah seperti untuk masalah loop, dll.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Jawaban:

207

Sama sekali tidak ada yang salah dengan kode yang Anda tulis. Keduanya some_functiondan someVarharus dapat diakses, jika tersedia dalam konteks di mana anonim

function() { some_function(someVar); } 

telah dibuat.

Periksa apakah lansiran memberi Anda nilai yang Anda cari, pastikan lansiran itu dapat diakses dalam lingkup fungsi anonim (kecuali jika Anda memiliki lebih banyak kode yang beroperasi pada someVarvariabel yang sama di samping panggilan ke addEventListener)

var someVar; 
someVar = some_other_function();
alert(someVar);
someObj.addEventListener("click", function(){
    some_function(someVar);
}, false);
Sergey Ilinsky
sumber
86
Ini tidak berfungsi untuk loop. Saya selalu mendapatkan nilai terbaru dan bukan yang termasuk dalam iterasi itu. Ada solusi?
iMatoria
6
Adakah yang tahu mengapa itu tidak bekerja dalam lingkaran? Apa alasan perilaku itu?
Morfidon
3
@ Morfidon karena fungsi dengan variabel global bertindak sebagai penutupan dalam javascript yang berarti mereka mengingat lingkungan di luar ruang lingkup leksikal mereka. Jika Anda hanya membuat fungsi berbeda di lingkungan yang sama maka mereka akan merujuk ke lingkungan yang sama.
bugwheels94
16
@Morfidon: Dalam satu lingkaran, nilai someVar bukanlah nilai yang dimilikinya ketika pendengar ditambahkan, tetapi nilai yang dimilikinya ketika pendengar dieksekusi. Ketika pendengar dieksekusi, loop sudah berakhir, sehingga nilai someVar akan menjadi nilai yang dimilikinya ketika loop berakhir.
www.admiraalit.nl
4
@iMatoria Saya baru saja menemukan bahwa membuat bound functionmenggunakan .bind()metode ini akan menyelesaikan masalah dengan loop developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Luke T O'Brien
354

Mengapa tidak mendapatkan argumen dari atribut target acara?

Contoh:

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 adalah bahasa berorientasi prototipe, ingat!

Zaloz
sumber
16
Ini adalah jawaban yang benar karena dapat digunakan setelah fungsi 'removeEventListener'.
user5260143
14
Bukankah seharusnya begitu evt.currentTarget.myParam? Jika ada elemen lain di dalam 'someInput', evt.targetmungkin merujuk ke elemen dalam. ( jsfiddle.net/qp5zguay/1 )
Herbertusz
Ini mempertahankan this! Menggunakan metode ini dalam naskah membutuhkan elemen untuk menjadi anyatau membuat sub jenis.
Old Badman Grey
1
Variabel saya terus kembali sebagai tidak terdefinisi ... ada pemikiran tentang cara memperbaikinya?
nomaam
1
Jika addEventListeneruntuk document, evt.target.myParamtidak bekerja untuk saya. Saya harus menggunakan evt.currentTarget.myParamsebagai gantinya.
turrican_34
67

Pertanyaan ini sudah tua tapi saya pikir saya akan menawarkan alternatif menggunakan ESB's .bind () - untuk anak cucu. :)

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

Perlu diketahui bahwa Anda perlu mengatur fungsi pendengar Anda dengan param pertama sebagai argumen yang Anda lewati untuk mengikat (fungsi Anda yang lain) dan param kedua sekarang adalah acara (bukan yang pertama, seperti seharusnya) .

Brian Moore
sumber
1
Function.prototype.bind () benar-benar cara terbaik untuk mengatasi masalah ini. Plus itu bekerja secara intuitif di dalam loop — Anda mendapatkan ruang lingkup leksikal yang Anda inginkan. Tidak ada fungsi anonim, IIFE , atau properti khusus yang ditempel pada objek.
Clint Pachl
Lihat pro dan kontra IIFE vs bind () .
Clint Pachl
1
Dengan menggunakan Function.prototype.bind()Anda tidak dapat menghapus pendengar acara , lebih baik menggunakan fungsi kari sebagai gantinya (lihat @ tomcek112 jawaban)
pldg
CATATAN: some_other_funcadalah variabel, Anda dapat memberikan nilai apa pun yang Anda inginkan some_func.
hitautodestruct
28

Anda bisa mengikat semua argumen yang diperlukan dengan 'bind':

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

Dengan cara ini Anda akan selalu mendapatkan event, arg1dan hal-hal lain diteruskan ke myPrettyHandler.

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

Oleksandr Tkalenko
sumber
Terima kasih! Sudah mencoba .bind()tetapi tanpa nol sebagai param pertama. yang tidak berhasil.
Larphoid
tidak perlu null, itu berfungsi dengan baik .bind(event, arg1), setidaknya dengan VueJS.
DevonDahon
27

Pertanyaan yang cukup lama dan tetapi saya memiliki masalah yang sama hari ini. Solusi terbersih yang saya temukan adalah menggunakan konsep currying.

Kode untuk itu:

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

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

Dengan menamai fungsi curried, Anda dapat memanggil Object.removeEventListener untuk membatalkan registrasi eventListener di waktu eksekusi selanjutnya.

tomcek112
sumber
4
Senang menemukan jawaban ini yang menyebutkan fungsi kari. Bagaimana Anda menghapus pendengar acara?
bob
3
Luar biasa melihat terminologi yang bagus. Anda harus dapat menghapus pendengar acara dengan memberi nama fungsi kari. saya akan mengusulkan edit.
Matius Brent
Jawaban ini akan mendaftarkan fungsi sebanyak addEventListener dipanggil, karena some_function (var) mengembalikan fungsi yang baru dibuat setiap saat.
Yahia
Saya tidak suka gagasan harus menyebutkan fungsi kari untuk menghapus pendengar karena kemudian Anda berurusan dengan 2 ruang nama yang berbeda yang harus Anda
ikuti
19

Anda bisa menambah dan menghapus eventlisteners dengan argumen dengan mendeklarasikan fungsi sebagai variabel.

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

newSrcadalah metode dengan myaudio sebagai parameter funcNameadalah variabel nama fungsi

Anda dapat menghapus pendengar dengan myaudio.removeEventListener('ended',func,false);

Ganesh Krishnan
sumber
12

Anda dapat melewatkan somevar dengan nilai (bukan dengan referensi) melalui fitur javascript yang dikenal sebagai closure :

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

Atau Anda dapat menulis fungsi wrap umum seperti 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'

Ini wrapEventCallback(func,var1,var2)seperti:

func.bind(null, var1,var2)
ahuigo
sumber
1
Terima kasih banyak atas jawaban ini! OP tidak mencari ini, tapi saya pikir orang-orang yang mengetik "Bagaimana cara menyampaikan args ke addEventListener" ke google akan mencari jawaban Anda. Hanya perlu sedikit penjelasan lebih lanjut :) Saya sedang mengeditnya.
Sindarus
9

someVarnilai harus dapat diakses hanya dalam some_function()konteks, bukan dari pendengar. Jika Anda ingin memilikinya dalam pendengar, Anda harus melakukan sesuatu seperti:

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

dan gunakan newVarsaja.

Cara lain adalah mengembalikan someVarnilai dari some_function()untuk menggunakannya lebih lanjut di pendengar (sebagai var lokal baru):

var someVar = some_function(someVar);
Mereka
sumber
9

Inilah cara lain (Yang ini bekerja di dalam untuk loop):

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

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

false);
Halo Dunia
sumber
2
Ini cara terbaik. Jelek, tetapi efektif dalam loop karena dengan mengirimkan argumen ke fungsi anonim akan menangkap var.
bob
9

Function.prototype.bind () adalah cara untuk mengikat fungsi target ke lingkup tertentu dan secara opsional menentukan thisobjek di dalam fungsi target.

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

Atau untuk menangkap beberapa lingkup leksikal, misalnya dalam satu lingkaran:

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

Akhirnya, jika thisparameter tidak diperlukan dalam fungsi target:

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

Menggunakan

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

Dan jika Anda ingin memberikan nilai khusus ke fungsi anonim ini, maka cara termudah untuk melakukannya adalah

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

Bekerja dengan baik di proyek saya. Semoga ini bisa membantu

kta
sumber
Ya, pasti membantu, karena juga mempertahankan ruang lingkup yang diharapkan dalam satu forlingkaran.
j4v1
4
    $form.addEventListener('submit', save.bind(null, data, keyword, $name.value, myStemComment));
    function save(data, keyword, name, comment, event) {

Ini adalah bagaimana saya melewati acara dengan benar.

kenyal
sumber
Luar biasa, ini adalah bagaimana saya hampir menyimpulkan - hanya keliru melewati acara tambahan di bind ketika tidak ada (seperti di sudut), yang secara otomatis datang dalam kasus ini.
Manohar Reddy Poreddy
3

Mengirim argumen ke fungsi callback eventListener membutuhkan pembuatan fungsi yang terisolasi dan meneruskan argumen ke fungsi yang terisolasi itu.

Inilah fungsi pembantu kecil yang bagus yang dapat Anda gunakan. Berdasarkan contoh "halo dunia" di atas.)

Satu hal yang juga dibutuhkan adalah mempertahankan referensi ke fungsi tersebut sehingga kita dapat menghapus pendengar dengan bersih.

// 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
sumber
Jawaban ini dari '15 tapi itulah yang saya butuhkan untuk menangani masalah ini dengan menggunakan hook useRef. Jika Anda menggunakan kait ref dan membutuhkan pendengar untuk itu Anda dapat membersihkan komponen unmount, ini dia. Argumen ke-4 storeFuncharus menjadi variabel referensi Anda. Letakkan penghapusan pendengar Anda dalam pengaruh yang efektif seperti ini dan Anda baik untuk pergi:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B
3

Salah satu caranya adalah melakukan ini dengan fungsi luar :

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

Metode membungkus fungsi anonim dalam tanda kurung dan segera memanggilnya disebut IIFE (Ekspresi Fungsi yang Segera Diminta)

Anda dapat memeriksa contoh dengan dua parameter di 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));
Felipe
sumber
3

Jika saya tidak salah menggunakan fungsi panggil bindsebenarnya menciptakan fungsi baru yang dikembalikan oleh bindmetode. Ini akan menyebabkan masalah Anda nanti atau jika Anda ingin menghapus pendengar acara, karena pada dasarnya seperti fungsi anonim:

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

Jadi ingatlah itu.

Jika Anda menggunakan ES6, Anda bisa melakukan hal yang sama seperti yang disarankan tetapi sedikit lebih bersih:

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

alternatif satu baris yang bagus

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

 //some action...

}
Gon82
sumber
2

Coba juga ini (IE8 + Chrome. Saya tidak tahu untuk 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

Semoga tidak ada kesalahan ketik :-)

DMike92
sumber
Seberapa sulitkah untuk memberikan jawaban yang lengkap? Haruskah saya menguji ini pada FF? Yah, aku tidak akan repot-repot ...
StefanNch
2

Saya terjebak dalam hal ini karena saya menggunakannya dalam satu lingkaran untuk menemukan elemen dan menambahkan listner ke dalamnya. Jika Anda menggunakannya dalam satu lingkaran, maka ini akan bekerja dengan sempurna

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

Pada 2019, banyak api berubah, jawaban terbaik tidak lagi berfungsi, tanpa memperbaiki bug.

bagikan beberapa kode kerja.

Terinspirasi oleh semua jawaban di atas.

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

Ada variabel khusus di dalam semua fungsi: argumen . Anda dapat melewatkan parameter Anda sebagai parameter anonim dan mengaksesnya (berdasarkan pesanan) melalui variabel argumen .

Contoh:

var someVar = some_other_function();
someObj.addEventListener("click", function(someVar){
    some_function(arguments[0]);
}, false);
StanE
sumber
Hmm ... Apa alasan untuk downvote? Jika itu bukan apa yang Anda cari, maka tolong jelaskan lebih jelas apa yang Anda maksudkan (saya tahu bahwa pertanyaannya sudah dijawab). Tetapi bukankah kode saya menjawab apa yang Anda minta? "Argumen" variabel khusus memberi Anda akses ke semua parameter di dalam suatu fungsi.
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
sumber
1

Pendekatan berikut bekerja dengan baik untuk saya. Dimodifikasi dari sini .

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

Jawaban berikut ini benar tetapi kode di bawah ini tidak berfungsi di IE8 jika Anda mengompres file js menggunakan yuicompressor. (Bahkan, masih sebagian besar orang AS menggunakan IE8)

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

Jadi, kita dapat memperbaiki masalah di atas sebagai berikut dan berfungsi dengan baik di semua browser

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

Semoga bermanfaat bagi seseorang yang mengompres file js di lingkungan produksi.

Semoga berhasil!!

Asik
sumber
0

Kode berikut ini berfungsi dengan baik untuk saya (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);
}

Keluaran:

0
1
2
Erg
sumber
Apa-apaan ini?
NiCk Newman
0

Anda membutuhkan:

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

Mungkin tidak optimal, tetapi cukup sederhana bagi mereka yang tidak paham js. Masukkan fungsi yang memanggil addEventListener ke dalam fungsinya sendiri. Dengan begitu nilai fungsi apa pun yang diteruskan ke dalamnya mempertahankan ruang lingkupnya sendiri dan Anda dapat mengulangi fungsi tersebut sebanyak yang Anda inginkan.

Contoh saya bekerja dengan membaca file karena saya perlu untuk menangkap dan membuat pratinjau gambar dan nama file. Butuh beberapa saat untuk menghindari masalah asinkron ketika menggunakan jenis unggah banyak file. Saya tidak sengaja akan melihat 'nama' yang sama pada semua render meskipun mengunggah file yang berbeda.

Awalnya, semua fungsi readFile () berada dalam fungsi readFiles (). Ini menyebabkan masalah pelingkupan asinkron.

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

hanya ingin menambahkan. jika ada yang menambahkan fungsi yang memperbarui kotak centang ke pendengar acara, Anda harus menggunakan event.targetalih-alih thismemperbarui kotak centang.

Bruce Tong
sumber
0

Saya memiliki pendekatan yang sangat sederhana. Ini mungkin bekerja untuk orang lain karena itu membantu saya. Itu adalah ... Ketika Anda memiliki beberapa elemen / variabel yang ditugaskan fungsi yang sama dan Anda ingin memberikan referensi, solusi paling sederhana adalah ...

function Name()
{

this.methodName = "Value"

}

Itu dia. Ini berhasil untuk saya. Sangat sederhana.

AAYUSH SHAH
sumber
-1

Alternatif lain, mungkin tidak seanggun penggunaan bind, tetapi valid untuk acara dalam satu lingkaran

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

Ini telah diuji untuk ekstensi google chrome dan mungkin e.srcElement harus diganti oleh e.source di browser lain

Saya menemukan solusi ini menggunakan komentar yang diposting oleh Imatoria tetapi saya tidak dapat menandainya sebagai berguna karena saya tidak memiliki reputasi yang cukup: D

fhuertas
sumber
-1

Solusi ini mungkin bagus untuk dicari

var some_other_function = someVar => function() {
}

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

atau bind bali juga akan baik

flovers
sumber