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

时间:2013-01-05 23:56:52

标签: javascript events parameter-passing addeventlistener

我想使用事件监听器来防止使用onclick函数在div内的div上冒泡。这是有效的,传递参数我的意图:

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

但是,我尝试在循环中创建多个事件侦听器:

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

它正确地将click函数分配给每个div,但每个div的第一个参数peram13。我期待3个不同的事件处理程序为i传递peram1的不同值。

为什么会这样?事件处理程序不是全部分开吗?

5 个答案:

答案 0 :(得分:19)

问题是闭包,因为JS没有块作用域(只有函数作用域)i不是您的想法,因为事件函数创建了另一个作用域,因此当您使用i时它已经是来自for循环的最新值。您需要保留i的价值。

使用IIFE:

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

使用forEach

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

答案 1 :(得分:3)

正如已经指出的那样,问题在于闭包和变量范围。确保传递正确值的一种方法是编写另一个返回所需函数的函数,将变量保存在正确的范围内。 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);
}

为了说明一种方法,你可以用一个对象来做到这一点:

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

或略有不同:

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

答案 2 :(得分:1)

javascript中的一切都是全球性的。它调用变量i,在循环后设置为3 ...如果在循环后将i设置为1000,那么您会看到每个方法调用为i生成1000

如果要维护状态,则应使用对象。让对象具有您为click方法指定的回调方法。

你提到为事件冒泡做这个...为了停止事件冒泡,你真的不需要它,因为它内置于语言中。如果您确实想要阻止事件冒泡,那么您应该使用传递给回调的stopPropagation()对象的event方法。

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

答案 3 :(得分:1)

这是因为关闭。

检查出来:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#Creating_closures_in_loops_A_common_mistake

示例代码和您的代码基本相同,对于那些不知道“封闭”的人来说,这是一个常见的错误。

简单来说,当你创建一个处理函数时,它不只是从外部环境访问变量i,而且还“记住”i

因此,当调用处理程序时,它将使用i,但变量i现在是for循环之后的2。

答案 4 :(得分:1)

我已经为这个问题苦苦挣扎了几个小时,现在我已经设法解决了。这是我的解决方案,使用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"));
};

注意:我尚未实际测试此代码。但是,我已经测试了codepen的所有重要功能,因此,如果上面的代码不起作用,我可能只是犯了一些拼写错误。这个概念应该仍然有效。

祝您编程愉快!