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

IT技术 javascript events parameter-passing addeventlistener
2021-03-16 04:53:47

我想使用事件侦听器来防止带有 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>

它正确地为每个 div 分配了点击功能,但每个 , 的第一个参数peram13我期待 3 个不同的事件处理程序都传递不同的iforperam1

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

5个回答

问题是闭包,因为 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
});
谢谢。我想知道为什么i会超出范围并返回一个undefined元素。
2021-05-01 04:53:47

正如已经指出的,问题与闭包和变量范围有关。确保传递正确值的一种方法是编写另一个返回所需函数的函数,将变量保存在正确的范围内。提琴手

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);
}
这非常有帮助,谢谢。对我来说有点高级,因为我刚刚开始在 javascript 中使用事件侦听器 - 我将进一步研究这一点。
2021-05-11 04:53:47

这是因为关闭。

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

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

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

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

@MoralesBatovski 感谢您的提醒。我刚刚编辑了我的帖子。
2021-05-08 04:53:47
鼓励链接到外部资源,但请在链接周围添加上下文,以便您的其他用户了解它是什么以及它为什么在那里。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。如何回答
2021-05-18 04:53:47

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

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

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

快乐编码!

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

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

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

function doStuff(event) {
    //Do things
    //stop bubbling
    event.stopPropagation();
}
感谢您帮助我解决 stopPropogation() - 它解决了我问题的另一部分。事件侦听器给我留下了与您建议的相同的冒泡问题 - 我正试图解决这个问题 - 但这里的这个答案也解决了我的冒泡问题。再次感谢:) 伙计!这个 Stackoverflow 是一群乐于助人的人!
2021-05-06 04:53:47
您关于“如果您在循环后将 i 设置为 1000,那么您会看到每个方法调用为 i 生成 1000”的评论也完全准确:)
2021-05-18 04:53:47