简体   繁体   English

在for循环中创建的Javascript多个动态addEventListener - 传递参数不起作用

[英]Javascript multiple dynamic addEventListener created in for loop - passing parameters not working

I want to use event listeners to prevent event bubbling on a div inside a div with onclick functions.我想使用事件侦听器来防止带有 onclick 函数的 div 内的 div 上的事件冒泡。 This works, passing parameters how I intended:这有效,按照我的意图传递参数:

<div onclick="doMouseClick(0, 'Dog', 'Cat');" id="button_id_0"></div>
<div onclick="doMouseClick(1, 'Dog', 'Cat');" id="button_id_1"></div>
<div onclick="doMouseClick(2, 'Dog', 'Cat');" id="button_id_2"></div>

<script>
function doMouseClick(peram1, peram2, peram3){
    alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}
</script>

However, I tried to create multiple event listeners in a loop with this:但是,我尝试使用以下方法在循环中创建多个事件侦听器:

<div id="button_id_0"></div>
<div id="button_id_1"></div>
<div id="button_id_2"></div>

<script>
function doMouseClick(peram1, peram2, peram3){
    alert("doMouseClick() called AND peram1 = "+peram1+" AND peram2 = "+peram2+" AND peram3 = "+peram3);
}

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

    for (var i=0; i<names.length; i++){

        document.getElementById(names[i]).addEventListener("click", function(){
        doMouseClick(i, "Dog", "Cat");

    },false);

}

</script>

It correctly assigns the click function to each div, but the first parameter for each, peram1 , is 3 .它正确地为每个 div 分配了点击功能,但每个 div 的第一个参数peram13 I was expecting 3 different event handlers all passing different values of i for peram1 .我期待 3 个不同的事件处理程序都为peram1传递不同的i值。

Why is this happening?为什么会这样? Are the event handlers not all separate?事件处理程序不是都是分开的吗?

Problem is closures, since JS doesn't have block scope (only function scope) i is not what you think because the event function creates another scope so by the time you use i it's already the latest value from the for loop.问题是闭包,因为 JS 没有块作用域(只有函数作用域) i不是您认为的那样,因为事件函数创建了另一个作用域,所以当您使用i它已经是for循环中的最新值。 You need to keep the value of i .您需要保留i的值。

Using an IIFE:使用 IIFE:

for (var i=0; i<names.length; i++) {
  (function(i) {
    // use i here
  }(i));
}

Using forEach :使用forEach

names.forEach(function( v,i ) {
  // i can be used anywhere in this scope
});

As pointed out already the problem is to do with closures and variable scope.正如已经指出的,问题与闭包和变量范围有关。 One way to make sure the right value gets passed is to write another function that returns the desired function, holding the variables within the right scope.确保传递正确值的一种方法是编写另一个返回所需函数的函数,将变量保存在正确的范围内。 jsfiddle提琴手

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

function getClickFunction(a, b, c) {
  return function () {
    doMouseClick(a, b, c)
  }
}
for (var i = 0; i < names.length; i++) {
  document.getElementById(names[i]).addEventListener("click", getClickFunction(i, "Dog", "Cat"), false);
}

And to illustrate one way you could do this with an object instead:并举例说明您可以使用对象执行此操作的一种方法:

var names = ['button_id_0', 'button_id_1', 'button_id_2'];

function Button(id, number) {
  var self = this;
  this.number = number;
  this.element = document.getElementById(id);
  this.click = function() {
    alert('My number is ' + self.number);
  }
  this.element.addEventListener('click', this.click, false);
}
for (var i = 0; i < names.length; i++) {
  new Button(names[i], i);
}

or slightly differently:或略有不同:

function Button(id, number) {
  var element = document.getElementById(id);
  function click() {
    alert('My number is ' + number);
  }
  element.addEventListener('click', click, false);
}
for (var i = 0; i < names.length; i++) {
  new Button(names[i], i);
}

It's because of closures.这是因为关闭。

Check this out: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake看看这个: https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

The sample code and your code is essentially the same, it's a common mistake for those don't know "closure".示例代码和你的代码本质上是一样的,对于那些不知道“闭包”的人来说,这是一个常见的错误。

To put it simple, when your create a handler function, it does not just accesses the variable i from the outer environment, but it also "remembers" i .简单来说,当您创建处理程序函数时,它不仅会从外部环境访问变量i ,而且还会“记住” i

So when the handler is called, it will use the i but the variable i is now, after the for-loop, 2.所以当处理程序被调用时,它将使用i但变量i现在是,在 for 循环之后,2。

I've been struggling with this problem myself for a few hours and now I've just now managed to solve it.我自己已经为这个问题苦苦挣扎了几个小时,现在我刚刚设法解决了它。 Here's my solution, using the function constructor :这是我的解决方案,使用函数构造函数

function doMouseClickConstructor(peram1, peram2, peram3){
    return new Function('alert("doMouseClick() called AND peram1 = ' + peram1 + ' AND peram2 = ' + peram2 + ' AND peram3 = ' + peram3 + ');');
}

for (var i=0; i<names.length; i++){
    document.getElementById(names[i]).addEventListener("click", doMouseClickConstructor(i,"dog","cat"));
};

Note: I havn't actually tested this code.注意:我还没有实际测试过这段代码。 I have however tested this codepen which does all the important stuff, so if the code above doesn't work I've probably just made some spelling error.然而,我已经测试了这个代码笔,它可以完成所有重要的事情,所以如果上面的代码不起作用,我可能只是犯了一些拼写错误。 The concept should still work.这个概念应该仍然有效。

Happy coding!快乐编码!

Everything is global in javascript.在 javascript 中,一切都是全局的。 It is calling the variable i which is set to 3 after your loop...if you set i to 1000 after the loop, then you would see each method call produce 1000 for i .它正在调用在循环后设置为 3 的变量i ……如果在循环后将i设置为 1000,那么您会看到每个方法调用为i产生 1000。

If you want to maintain state, then you should use objects.如果你想保持状态,那么你应该使用对象。 Have the object have a callback method that you assign to the click method.让对象具有分配给 click 方法的回调方法。

You mentioned doing this for event bubbling...for stopping event bublling, you really do not need that, as it is built into the language.你提到这样做是为了事件冒泡……为了停止事件冒泡,你真的不需要那个,因为它内置于语言中。 If you do want to prevent event bubbling, then you should use the stopPropagation() method of the event object passed to the callback.如果您确实想防止事件冒泡,那么您应该使用传递给回调的event对象的stopPropagation()方法。

function doStuff(event) {
    //Do things
    //stop bubbling
    event.stopPropagation();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM