在初学JS时我们都会遇到这样一个问题:
在为一组列表元素(5个li)绑定事件时,想要获取该元素对应的索引信息。
var lis = document.querySelectorAll("li");
for(var i = 0; i < lis.length; i++){
lis[i].onclick = function(){
alert(i)
}
}
//执行之后,发现每一个li点击之后弹出的都是5,并不是我们期望的0,1,2,3和4。
原因何在?
其实,我们一定要搞清楚,代码的编写和执行是分开的。换言之,事件的执行一定是分两个过程:
注册过程,绑定过程
触发了,执行注册好代码
注册绑定过程
在绑定的过程中,for语句一定会执行。但是函数中的alert是不会执行的
循环完毕,相当于是如下代码:
lis[0].onclick=function(){
alert(i)
}
lis[1].onclick=function(){
alert(i)
}
lis[2].onclick=function(){
alert(i)
}
lis[3].onclick=function(){
alert(i)
}
lis[4].onclick=function(){
alert(i)
}
循环完毕,i的值已经是5了。注意此时绑定的事件监听函数并未执行。)
触发过程
然后,当我们点击具体的某一个li时,才会真正的执行function代码
此时,i的值就是外部的i,都会弹出5
解决方法:
在循环绑定事件时给每一个li元素对象增加一个索引值
利用自执行函数或闭包
- 增加索引
for(var i=0;i<lis.length;i++){
lis[i].index = i;
lis[i].onclick=function(){
alert(this.index)
}
}
- 自执行函数和闭包
for(var i=0;i<lis.length;i++){
lis[i].onclick=function(n){
return function(){
alert(n);
}
}(i)
}
这里之所以,可以就是因为针对每一个li绑定的处理函数中,都有一个自己是局部变量,值依次是i循环的值,分别是0,1,2,3和4。
- let 的方式 (ES6新增)
for(let i=0;i<lis.length;i++){
lis[i].onclick=function(){
alert(i)
}
}
尽管现在看到只有一个i,但是这个i不是全局的i,而是每一个事件处理函数自己的i。
实际上,let 的出现,其实就是为了解决这一类的问题。
总结
想清楚这个问题的关键就在于,要明白用户触发事件时,循环绑定事件这样的一个过程早已结束,所以此时事件处理程序中使用的i值,并非我们绑定时候的i值。其实也就是绑定时,事件处理程序并未执行,它的执行是在for循环绑定事件完成之后。