如何将参数传递给addEventListener侦听器函数?


304

情况有点像-

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

问题在于,的值someVar在的侦听器函数中不可见,在该函数addEventListener中它可能被视为新变量。


8
关于此事的非常清晰的文章:toddmotto.com/avoiding-anonymous-javascript-functions
大雄

不是最干净的方法,但是可以完成工作。请注意,如果someVar只能是数字或文本:eval('someObj.addEventListener(“ click”,function(){some_function('+ someVar +');});');
Ignas2526 2014年

今天刚刚有这个问题-在这里给出的解决方案是正确的(其他的解决方案有问题,如对循环问题等) - stackoverflow.com/a/54731362/984471
马诺哈尔·雷迪Poreddy

Answers:


206

您编写的代码绝对没有错。双方some_functionsomeVar应访问的,以防他们在上下文中可用的匿名

function() { some_function(someVar); } 

已创建。

检查警报是否为您提供了您一直在寻找的值,请确保它可以在匿名函数的范围内访问(除非您someVar对调用旁边有更多对相同变量进行操作的代码addEventListener

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

86
这在for循环中不起作用。我总是得到最新的值,而不是那个迭代的值。有什么办法吗?
iMatoria

6
有人知道为什么它不能循环工作吗?这种行为的原因是什么?
Morfidon

3
@Morfidon,因为具有全局变量的函数在javascript中充当闭包,这意味着它们会记住其词法范围之外的环境。如果只是在同一环境中创建不同的功能,则它们将引用同一环境。
bugwheels94

16
@Morfidon:在循环中,someVar的值不是添加侦听器时的值,而是执行侦听器时的值。当执行侦听器时,循环已经结束,因此someVar的值将是循环结束时的值。
www.admiraalit.nl

4
@iMatoria我刚刚发现,创造一个bound function使用.bind()方法解决循环问题developer.mozilla.org/en/docs/Web/JavaScript/Reference/...
卢克牛逼奥布莱恩

353

为什么不仅仅从事件的target属性获取参数?

例:

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是一种面向原型的语言,请记住!


16
这是正确的答案,因为它让我们在'removeEventListener'函数之后使用。
user5260143

14
不是evt.currentTarget.myParam吗?如果“ someInput”内部还有另一个元素,则evt.target可以引用内部元素。(jsfiddle.net/qp5zguay/1
Herbertusz

这样保存this!在打字稿中使用此方法需要使元素成为any或创建子类型。
Old Badman Gray

1
我的变量不断返回未定义状态...关于如何解决该问题的任何想法?
nomaam

1
如果addEventListener是为了documentevt.target.myParam对我没用。我不得不使用evt.currentTarget.myParam
turrican_34 '19

67

这个问题很旧,但我想我会提供使用ES5的.bind()的替代方法-以供后代使用。:)

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

请注意,您需要将第一个参数设置为您要传递给bind(您的其他函数)的参数的侦听器函数,而第二个参数现在是事件(而不是原来的第一个参数) 。


1
Function.prototype.bind()实际上是解决此问题的最佳方法。另外,它在循环内直观地工作-您可以获得所需的词法范围。没有附加到对象的匿名函数,IIFE或特殊属性。
克林特·帕奇

请参阅IIFE与bind()的优缺点。
克林特·帕奇

1
通过使用Function.prototype.bind()您无法删除事件侦听器,最好改用currying函数(请参阅@ tomcek112答案)
pldg

注意:some_other_func是一个变量,您可以传递想要的任何值some_func
hitautodestruct

28

您只需将所有必要的参数与'bind'绑定即可:

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

通过这种方式,你将永远得到eventarg1传递,和其他的东西myPrettyHandler

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


谢谢!已经尝试过.bind()但没有将null作为第一个参数。这没有用。
Larphoid

不需要null.bind(event, arg1)至少可以与VueJS一起使用。
DevonDahon

27

相当老的问题,但今天我有同样的问题。我发现最干净的解决方案是使用currying概念

该代码:

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

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

通过命名curried函数,它允许您调用Object.removeEventListener以便在以后的执行时注销eventListener。


4
很高兴遇到这个回答,提到了咖喱函数。但是,如何删除事件监听器?
bob

3
很棒,能看到好的术语。您应该能够通过命名curried函数来删除事件侦听器。我会提出修改。
马修·布伦特

这个答案将注册函数的次数与调用addEventListener的次数相同,因为some_function(var)每次都会返回一个新创建的函数。
Yahia

我不喜欢必须命名curried函数以删除侦听器的想法,然后您要处理2个您必须跟踪的diff命名空间
oldboy

19

您可以通过将函数声明为变量来添加和删除带参数的事件侦听器。

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

newSrc是以myaudio作为参数的方法 funcName是函数名称变量

您可以使用以下方法删除监听器 myaudio.removeEventListener('ended',func,false);


12

您可以通过称为闭包的JavaScript功能按值(而不是按引用)传递somevar :

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

或者,您可以编写一个通用的包装函数,例如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'

这里wrapEventCallback(func,var1,var2)是这样的:

func.bind(null, var1,var2)

1
非常感谢您的回答!OP并不是在寻找这个,但是我认为在Google中输入“如何将args传递给addEventListener的人”会寻找您的答案。它只需要更多说明:)我正在编辑它。
Sindarus

9

someVar值只能在some_function()上下文中访问,不能从侦听器访问。如果您希望将它包含在侦听器中,则必须执行以下操作:

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

newVar改为使用。

另一种方法是返回someVar值,some_function()以供在侦听器中进一步使用它(作为新的本地变量):

var someVar = some_function(someVar);

9

这是另一种方式(这种方式在for循环内有效):

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

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

false);

2
这是最好的方法。丑陋,但在循环内有效,因为通过向匿名函数发送参数将捕获var。
鲍勃,2015年

9

Function.prototype.bind()是将目标函数绑定到特定作用域并有选择地this在目标函数内定义对象的方法。

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

或捕获某些词汇范围,例如在循环中:

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

最后,如果this目标函数中不需要该参数:

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

7

采用

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

如果您要将任何自定义值传递给此匿名函数,那么最简单的方法是

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

在我的项目中完美运行。希望这会有所帮助


是的,绝对有帮助,因为它还在for循环中保持了预期的范围。
j4v1

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

这就是我正确传递事件的方式。


太好了,这就是我几乎要得出的结论-只是在绑定不存在时错误地在绑定中传递了额外事件(例如在angular中),在这种情况下会自动发生。
Manohar Reddy Poreddy

3

将参数发送到eventListener的回调函数需要创建一个隔离的函数,并将参数传递给该隔离的函数。

这是您可以使用的一个不错的小助手功能。基于上面的“ hello world'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);

这个答案来自15年,但这正是我使用useRef挂钩处理此问题所需要的。如果您使用的是ref钩子,并且需要一个侦听器以清理组件卸载,就可以了。的第四个arg storeFunc应该是您的ref变量。像这样将您的收听者移除放入一个useEffect中,您就可以开始:useEffect(() => { return () => { window.removeEventListener('scroll', storeFunc, false); } }, [storeFunc])
Rob B

3

一种方法是使用外部函数执行此操作:

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

这种将匿名函数包装在括号中并立即调用的方法称为IIFE(立即调用函数表达式)

您可以在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

如果我没有记错,那么使用调用该函数bind实际上会创建一个由该bind方法返回的新函数。这将在以后或您想要删除事件侦听器时给您带来问题,因为它基本上就像一个匿名函数:

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

所以请记住这一点。

如果您使用的是ES6,则可以按照建议的方法进行操作,但是更加简洁:

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

3

不错的一线替代

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

 //some action...

}

2

也尝试以下方法(IE8 + Chrome。我不知道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

希望没有错别字:-)


给出完整的答案有多难?我应该在FF上测试吗?好吧,我不会打扰...
StefanNch

2

我陷入了困境,因为我在循环中使用它来查找元素并向其中添加列表器。如果您要循环使用它,那么它将完美地工作

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年,许多api更改,最佳答案不再有效,并且没有修复错误。

共享一些工作代码。

受到以上所有答案的启发。

 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

所有函数内部都有一个特殊变量:arguments。您可以将参数作为匿名参数传递,并通过参数(按顺序)访问它们变量。

例:

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

嗯。如果不是您要查找的内容,请更清楚地解释您的意思(我知道问题已经回答了)。但是我的代码不是在回答您的要求吗?特殊变量“参数”使您可以访问函数内的所有参数。
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

以下方法对我来说效果很好。从这里修改。

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

以下答案是正确的,但是如果假设您使用yuicompressor压缩了js文件,则以下代码在IE8中不起作用。(实际上,大多数美国人仍然使用IE8)

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

因此,我们可以按以下方式解决上述问题,并且在所有浏览器中都可以正常工作

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

希望对在生产环境中压缩js文件的人有用。

祝好运!!


0

以下代码对我来说效果很好(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);
}

输出:

0
1
2

这到底是什么?
NiCk Newman


0

可能不是最佳选择,但对于那些不精通js的人来说足够简单。将调用addEventListener的函数放入其自己的函数中。这样,传递给它的任何函数值都将保持其自身的作用域,并且您可以根据需要迭代该函数。

示例我需要捕获并渲染图像和文件名的预览,然后进行了文件读取。在使用多文件上传类型时,我花了一些时间来避免异步问题。即使上传了不同的文件,我也会在所有渲染器上意外看到相同的“名称”。

最初,所有readFile()函数都在readFiles()函数内。这引起了异步作用域问题。

    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

只是想补充。如果有人添加了将复选框更新为事件侦听器的功能,则您将不得不使用event.target而不是this更新复选框。


0

我的方法很简单。这可能对其他人有用,因为它帮助了我。它是...当您为多个元素/变量分配了相同的功能并且要传递引用时,最简单的解决方案是...

function Name()
{

this.methodName = "Value"

}

而已。它为我工作。 很简单。


-1

其他替代方法,也许不如使用bind优雅,但它对于循环中的事件有效

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扩展程序的测试,可能在其他浏览器中e.srcElement必须替换为e.source。

我使用Imatoria发布的评论找到了该解决方案,但由于我没有足够的声誉而无法将其标记为有用的:D


-1

这个解决方案可能适合寻找

var some_other_function = someVar => function() {
}

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

或绑定变量也将很好

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.