在循环中分配EventListener

时间:2013-06-13 03:14:44

标签: javascript

我使用这行代码将事件监听器添加到通过forloop创建的div:

for(var i in mail){
   //create div
   parent.addEventListener("click",function(){read_msg(mail[i].id);},false);
   //append to parent
}

这导致mail[i].id成为所有人的最后一个ID的问题。我已经阅读了一些如何解决它的例子,但我发现它仍然很混乱。

我被建议解决方案:

(function(){read_msg(mail[this].id)}).bind(i);

但是我被告知这不是一个很好用的解决方案,希望有人可以解释你如何让read_msg保持id的正确值?在解决方案方面似乎总是有些混乱。

3 个答案:

答案 0 :(得分:1)

这是因为您在事件处理函数中使用了闭包变量i

变量i位于外部函数的范围内,即变量i只有一个实例。当调用处理程序方法并且访问i时,javascript将首先查看处理程序函数的范围,如果它没有找到变量,那么它将查看父闭包范围,然后它将在父范围。

在父作用域中,当循环执行时,值会不断更改i的值,这就是为什么所有回调都具有i的相同值。

这里的解决方案是创建一个本地闭包

for(var i in mail){
    (function(myvar){
        parent.addEventListener("click",function(){read_msg(mail[myvar].id);},false);
        //append to parent
    })(i);
}

我们在这里做的是我们有一个立即调用的函数表达式,我们将i的值作为参数myvar传递给它。因此循环中的每次迭代都将创建一个独立的闭包。

答案 1 :(得分:1)

你可以在大多数浏览器中使用Object + Array方法来解决js中讨厌的循环范围“bug”:

 function addClick(key){
   //create div
   parent.addEventListener("click",function(){read_msg(mail[key].id);},false);
   //append to parent
}   

Object.keys(mail).forEach(addClick);

由于函数具有scope和forEach eats函数,因此在使用Array方法时不需要额外的anon包装器。

如果你想全力以赴的新热点:

 function addClick(key){

   parent.addEventListener("click", this.method.bind( this.elm, this.source[key].id ), false);

}   

Object.keys(mail).forEach(addClick, {elm:parent, source: mail, method:read_msg });

你反转键的源对象以允许使用“mail”以外的对象,“parent”以外的元素来附加事件,以及“read_msg”以外的方法,所有这些都不需要触及逻辑或使用单词“return”...基本上,无论你在传统的C风格for循环初始化程序中设置什么,你都可以移动到这个东西并在需要时重新应用逻辑。

答案 2 :(得分:0)

因为每个循环迭代定义为事件处理程序的每个匿名函数将与所有其他函数共享相同的作用域,它们将引用与您尝试显示的消息的数组地址相同的var(i)。因为您正在使用每个循环重新定义var i,所以您将始终在每个单击事件中看到消息数组中显示的最后一条消息,因为分配给i的最后一个值将是" mail"阵列。

继承人如何解决它:

var helper = function(index) {
    parent.addEventListener("click", function(){read_msg(mail[index].id);},false);
}

for(var i in mail) {
    helper(i);
}