Làm thế nào để truyền đối số cho hàm listener addEventListener?


304

Tình hình có phần giống-

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

Vấn đề là giá trị của someVarkhông thể nhìn thấy bên trong hàm nghe của addEventListener, trong đó nó có thể được coi là một biến mới.


8
Một bài viết rất rõ ràng về vấn đề này: babmotto.com/avoiding-anonymous-javascript-fifts
Nobita

Không phải là cách sạch nhất, nhưng làm công việc. Lưu ý rằng nếu someVar chỉ có thể là chữ số hoặc văn bản: eval ('someObj.addEventListener ("click", function () {some_function (' + someVar + ');});');
Ignas2526

Chỉ có vấn đề này hôm nay - giải pháp được đưa ra ở đây là chính xác (các giải pháp khác có vấn đề như vấn đề vòng lặp, v.v.) - stackoverflow.com/a/54731362/984471
Manohar Reddy Poreddy

Câu trả lời:


207

Hoàn toàn không có gì sai với mã bạn đã viết. Cả hai some_functionsomeVarnên có thể truy cập được, trong trường hợp chúng có sẵn trong bối cảnh ẩn danh

function() { some_function(someVar); } 

đã được tạo ra.

Kiểm tra xem cảnh báo có cung cấp cho bạn giá trị mà bạn đang tìm kiếm hay không, hãy chắc chắn rằng nó sẽ có thể truy cập được trong phạm vi chức năng ẩn danh (trừ khi bạn có thêm mã hoạt động trên cùng một someVarbiến bên cạnh lệnh gọi addEventListener)

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

86
Điều này không hoạt động trong vòng lặp. Tôi luôn nhận được giá trị mới nhất và không phải là giá trị thuộc về lần lặp đó. Bất kì giải pháp nào?
iMatoria

6
Bất cứ ai cũng biết tại sao nó không hoạt động trong vòng lặp? Lý do của hành vi đó là gì?
Morfidon

3
@Morfidon vì hàm với các biến toàn cục đóng vai trò là đóng trong javascript, có nghĩa là chúng nhớ môi trường bên ngoài phạm vi từ vựng của chúng. Nếu bạn chỉ tạo chức năng khác nhau trong cùng một môi trường thì chúng sẽ tham chiếu đến cùng một môi trường.
bugwheels94

16
@Morfidon: Trong một vòng lặp, giá trị của someVar không phải là giá trị mà nó có khi người nghe được thêm vào, mà là giá trị nó có khi trình nghe được thực thi. Khi người nghe được thực thi, vòng lặp đã kết thúc, vì vậy giá trị của someVar sẽ là giá trị mà nó có khi vòng lặp kết thúc.
www.admiraalit.nl

4
@iMatoria Tôi vừa phát hiện ra rằng việc tạo phương thức bound functionbằng cách sử dụng .bind()sẽ giải quyết vấn đề với các vòng lặp developer.mozilla.org/en/docs/Web/JavaScript/Reference/
Luke T O'Brien

353

Tại sao không chỉ lấy các đối số từ thuộc tính đích của sự kiện?

Thí dụ:

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 là một ngôn ngữ hướng nguyên mẫu, hãy nhớ!


16
Đây là câu trả lời đúng vì chúng ta hãy sử dụng sau hàm 'removeEventListener'.
dùng5260143

14
Có nên không evt.currentTarget.myParam? Nếu có một yếu tố khác bên trong 'someInput', thì evt.targetcó thể được tham chiếu đến yếu tố bên trong. ( jsfiddle.net/qp5zguay/1 )
Herbertusz

Điều này bảo tồn this! Sử dụng phương thức này trong bản thảo đòi hỏi phần tử phải anyhoặc tạo một kiểu con.
Old Badman Grey

1
Các biến của tôi tiếp tục quay trở lại là không xác định ... bạn có suy nghĩ gì về cách khắc phục điều đó không?
nomaam

1
Nếu addEventListenerlà cho document, evt.target.myParamđã không làm việc cho tôi. Tôi đã phải sử dụng evt.currentTarget.myParamthay thế.
turrican_34

67

Câu hỏi này đã cũ nhưng tôi nghĩ tôi sẽ cung cấp một giải pháp thay thế bằng cách sử dụng .bind () của ES5 cho hậu thế. :)

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

Chỉ cần lưu ý rằng bạn cần thiết lập chức năng trình nghe của mình với tham số đầu tiên là đối số bạn đang chuyển sang liên kết (chức năng khác của bạn) và tham số thứ hai bây giờ là sự kiện (thay vì đầu tiên, như đã từng) .


1
Function.prototype.bind () thực sự là cách tốt nhất để giải quyết vấn đề này. Thêm vào đó, nó hoạt động trực quan bên trong các vòng lặp Bạn có được phạm vi từ vựng bạn muốn. Không có chức năng ẩn danh, IIFE hoặc thuộc tính đặc biệt nào được xử lý cho các đối tượng.
Clint Pachl

Xem những ưu và nhược điểm của IIFE so với bind () .
Clint Pachl

1
Bằng cách sử dụng, Function.prototype.bind()bạn không thể loại bỏ trình nghe sự kiện , tốt hơn là sử dụng chức năng currying (xem câu trả lời @ tomcek112)
pldg

LƯU Ý: some_other_funclà một biến, bạn có thể chuyển bất kỳ giá trị nào bạn muốn some_func.
hitautodesturation

28

Bạn chỉ có thể liên kết tất cả các đối số cần thiết với 'bind':

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

Bằng cách này bạn sẽ luôn luôn nhận được event, arg1, và các công cụ khác để thông qua myPrettyHandler.

http://passy.svbussy.com/partial-application-in-javascript-USE-bind


Cảm ơn! Đã thử .bind()nhưng không có null như param đầu tiên. mà không làm việc
Ấu trùng

không cần null, nó hoạt động tốt với .bind(event, arg1), ít nhất là với VueJS.
DevonDahon

27

Câu hỏi khá và cũ nhưng tôi đã có cùng một vấn đề ngày hôm nay. Giải pháp sạch nhất tôi tìm thấy là sử dụng khái niệm cà ri.

Mã cho điều đó:

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

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

Bằng cách đặt tên hàm curried, nó cho phép bạn gọi Object.removeEventListener để hủy đăng ký eventListener sau một thời gian thực hiện.


4
Vui mừng khi gặp câu trả lời này đề cập đến chức năng curried. Làm thế nào bạn sẽ loại bỏ người nghe sự kiện mặc dù?
bob

3
Tuyệt vời để xem thuật ngữ tốt. Bạn sẽ có thể loại bỏ trình nghe sự kiện bằng cách đặt tên hàm curried. Tôi sẽ đề xuất một chỉnh sửa.
Matthew Brent

Câu trả lời này sẽ đăng ký hàm nhiều lần khi addEventListener được gọi, vì some_function (var) đang trả về một hàm mới được tạo mỗi lần.
Yahia

Tôi không thích ý tưởng phải đặt tên cho hàm bị khóa để loại bỏ người nghe, sau đó bạn xử lý 2 không gian tên khác nhau mà bạn phải theo dõi
oldboy

19

Bạn có thể thêm và xóa các danh sách sự kiện bằng các đối số bằng cách khai báo một hàm là một biến.

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

newSrclà phương thức với myaudio là tham số funcNamelà biến tên hàm

Bạn có thể loại bỏ người nghe bằng myaudio.removeEventListener('ended',func,false);


12

Bạn có thể vượt qua somevar theo giá trị (không phải bằng tham chiếu) thông qua tính năng javascript được gọi là bao đóng :

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

Hoặc bạn có thể viết một hàm bọc phổ biến như 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'

Đây wrapEventCallback(func,var1,var2)là như:

func.bind(null, var1,var2)

1
Cảm ơn rất nhiều cho câu trả lời này! OP đã không tìm kiếm điều này, nhưng tôi nghĩ rằng những người gõ "Cách truyền args vào addEventListener" vào google sẽ tìm kiếm câu trả lời của bạn. Nó chỉ cần thêm một chút giải thích :) Tôi đang chỉnh sửa nó.
Sindarus

9

someVargiá trị chỉ có thể được truy cập trong some_function()ngữ cảnh, không phải từ người nghe. Nếu bạn muốn có nó trong trình nghe, bạn phải làm một cái gì đó như:

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

và sử dụng newVarthay thế.

Một cách khác là trả về someVargiá trị từ some_function()việc sử dụng nó thêm trong trình nghe (dưới dạng var cục bộ mới):

var someVar = some_function(someVar);

9

Đây là một cách khác (Cách này hoạt động bên trong các vòng lặp):

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

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

false);

2
Đây la cach tôt nhât. Xấu xí, nhưng hiệu quả trong các vòng lặp vì bằng cách gửi một đối số vào một hàm ẩn danh sẽ nắm bắt được var.
bob

9

Function.prototype.bind () là cách để liên kết một hàm mục tiêu với một phạm vi cụ thể và tùy ý xác định thisđối tượng trong hàm mục tiêu.

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

Hoặc để nắm bắt một số phạm vi từ vựng, ví dụ như trong một vòng lặp:

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

Cuối cùng, nếu thistham số không cần thiết trong hàm mục tiêu:

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

7

Sử dụng

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

Và nếu bạn muốn chuyển bất kỳ giá trị tùy chỉnh nào vào hàm ẩn danh này thì cách dễ nhất để làm điều đó là

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

Hoạt động hoàn hảo trong dự án của tôi. Hy vọng điều này sẽ giúp


Vâng, chắc chắn có ích, vì nó cũng duy trì phạm vi dự kiến ​​trong một forvòng lặp.
j4v1

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

Đây là cách tôi có sự kiện thông qua đúng.


Tuyệt vời, đây là cách tôi gần như đã kết luận - chỉ cần sai một sự kiện bổ sung trong liên kết khi nó không ở đó (như trong góc), tự động xuất hiện trong trường hợp này.
Manohar Reddy Poreddy

3

Gửi các đối số đến hàm gọi lại của eventListener yêu cầu tạo một hàm bị cô lập và chuyển các đối số đến hàm bị cô lập đó.

Đây là một chức năng trợ giúp nhỏ đẹp mà bạn có thể sử dụng. Dựa trên ví dụ "xin chào thế giới" ở trên.)

Một điều cũng cần thiết là duy trì một tham chiếu đến chức năng để chúng ta có thể loại bỏ người nghe một cách sạch sẽ.

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

Câu trả lời này là từ '15 nhưng đó chính xác là những gì tôi cần để xử lý vấn đề này bằng cách sử dụng hook useRef. Nếu bạn đang sử dụng hook ref và cần một trình lắng nghe cho nó mà bạn có thể dọn sạch trên phần kết nối, thì đây chính là nó. Đối số thứ 4 storeFuncnên là biến ref của bạn. Đặt loại bỏ người nghe của bạn trong một cách sử dụng Hiệu quả như thế này và bạn sẽ thấy tốt:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

Một cách là làm điều này với một chức năng bên ngoài :

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

Phương pháp gói hàm ẩn danh này trong ngoặc đơn và gọi nó ngay lập tức được gọi là IIFE (Biểu thức hàm được gọi ngay lập tức)

Bạn có thể kiểm tra một ví dụ với hai tham số trong 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

Nếu tôi không nhầm sử dụng cách gọi hàm với bindthực sự tạo ra một hàm mới được bindphương thức trả về . Điều này sẽ gây ra sự cố cho bạn sau này hoặc nếu bạn muốn xóa trình lắng nghe sự kiện, vì về cơ bản nó giống như một chức năng ẩn danh:

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

Vì vậy, hãy ghi nhớ điều đó.

Nếu bạn đang sử dụng ES6, bạn có thể làm tương tự như được đề xuất nhưng sạch hơn một chút:

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

3

thay thế một dòng tốt đẹp

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

 //some action...

}

2

Cũng thử những thứ này (IE8 + Chrome. Tôi không biết về 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

Hy vọng không có lỗi chính tả :-)


Làm thế nào khó khăn để đặt một câu trả lời đầy đủ? Tôi có nên thử nghiệm điều này trên FF không? Chà, tôi sẽ không làm phiền ...
StefanNch

2

Tôi đã bị mắc kẹt trong điều này khi tôi đang sử dụng nó trong một vòng lặp để tìm các yếu tố và thêm người nghe vào nó. Nếu bạn đang sử dụng nó trong một vòng lặp, thì điều này sẽ hoạt động hoàn hảo

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

Năm 2019, rất nhiều thay đổi api, câu trả lời tốt nhất không còn hoạt động, không sửa lỗi.

chia sẻ một số mã làm việc.

Lấy cảm hứng từ tất cả các câu trả lời trên.

 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ó một biến đặc biệt bên trong tất cả các hàm: đối số . Bạn có thể truyền tham số của mình dưới dạng tham số ẩn danh và truy cập chúng (theo thứ tự) thông qua biến đối số.

Thí dụ:

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

Hmm ... lý do của downvote là gì? Nếu đó không phải là những gì bạn đang tìm kiếm, xin vui lòng giải thích rõ hơn ý của bạn (tôi biết rằng câu hỏi đã được trả lời). Nhưng không phải mã của tôi trả lời những gì bạn yêu cầu? Biến "đối số" đặc biệt cho phép bạn truy cập vào tất cả các tham số bên trong hàm.
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

Cách tiếp cận sau đây làm việc tốt cho tôi. Sửa đổi từ đây .

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

Câu trả lời sau đây là đúng nhưng đoạn mã dưới đây không hoạt động trong IE8 nếu giả sử bạn nén tệp js bằng yuicompressor. (Trên thực tế, vẫn còn hầu hết người dân Hoa Kỳ sử dụng IE8)

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

Vì vậy, chúng tôi có thể khắc phục sự cố ở trên như sau và nó hoạt động tốt trong tất cả các trình duyệt

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

Hy vọng, nó sẽ hữu ích cho một số người đang nén tệp js trong môi trường sản xuất.

Chúc may mắn!!


0

Đoạn mã sau hoạt động tốt với tôi (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);
}

Đầu ra:

0
1
2

Thế giới này có gì?
NiCk Newman

0

Bạn cần:

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

0

Có lẽ không tối ưu, nhưng đủ đơn giản cho những người không siêu hiểu biết js. Đặt hàm gọi addEventListener vào hàm riêng của nó. Bằng cách đó, bất kỳ giá trị hàm nào được truyền vào nó đều duy trì phạm vi riêng của chúng và bạn có thể lặp lại hàm đó nhiều như bạn muốn.

Ví dụ tôi đã làm việc với việc đọc tệp khi tôi cần để chụp và hiển thị bản xem trước của hình ảnh và tên tệp. Tôi đã mất một lúc để tránh các vấn đề không đồng bộ khi sử dụng loại tải lên nhiều tệp. Tôi sẽ vô tình nhìn thấy cùng một 'tên' trên tất cả các kết xuất mặc dù tải lên các tệp khác nhau.

Ban đầu, tất cả hàm readFile () đều nằm trong hàm readFiles (). Điều này gây ra các vấn đề phạm vi không đồng bộ.

    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

chỉ muốn thêm. nếu bất cứ ai đang thêm chức năng cập nhật các hộp kiểm cho người nghe sự kiện, bạn sẽ phải sử dụng event.targetthay vì thiscập nhật các hộp kiểm.


0

Tôi có cách tiếp cận rất đơn giản. Điều này có thể làm việc cho những người khác vì nó đã giúp tôi. Đó là ... Khi bạn có nhiều phần tử / biến được gán cùng một hàm và bạn muốn truyền tham chiếu, giải pháp đơn giản nhất là ...

function Name()
{

this.methodName = "Value"

}

Đó là nó. Nó làm việc cho tôi. Quá đơn giản.


-1

Thay thế khác, có lẽ không thanh lịch như việc sử dụng liên kết, nhưng nó hợp lệ cho các sự kiện trong một vòng lặp

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

Nó đã được thử nghiệm cho các tiện ích mở rộng chrome của google và có thể phải thay thế e.srcEuity bằng e.source trong các trình duyệt khác

Tôi đã tìm thấy giải pháp này bằng cách sử dụng nhận xét được đăng bởi Imatoria nhưng tôi không thể đánh dấu nó là hữu ích vì tôi không có đủ danh tiếng: D


-1

Giải pháp này có thể tốt cho việc tìm kiếm

var some_other_function = someVar => function() {
}

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

hoặc ràng buộc valiable cũng sẽ tốt

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.