5

我想使用事件侦听器来防止带有 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 个不同的事件处理程序都传递不同的ifor值peram1

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

4

5 回答 5

20

问题是闭包,因为 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
});
于 2013-01-06T00:04:31.680 回答
2

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

这是因为关闭。

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

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

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

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

于 2016-06-24T15:12:46.267 回答
1

我自己已经为这个问题苦苦挣扎了几个小时,现在我刚刚设法解决了它。这是我的解决方案,使用函数构造函数

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

注意:我还没有实际测试过这段代码。然而,我已经测试了这个代码笔,它可以完成所有重要的事情,所以如果上面的代码不起作用,我可能只是犯了一些拼写错误。这个概念应该仍然有效。

快乐编码!

于 2020-06-24T14:51:23.737 回答
0

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

如果你想保持状态,那么你应该使用对象。让对象具有分配给 click 方法的回调方法。

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

function doStuff(event) {
    //Do things
    //stop bubbling
    event.stopPropagation();
}
于 2013-01-06T00:03:18.163 回答