for循环绑定事件解决方法
首先我们来看一段代码
<body>
<ul id="ul">
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
<li>5555</li>
</ul>
<script type="text/javascript">
var oUl = document.getElementById("ul");
var aLi = oUl.getElementsByTagName("li");
for(var i = 0; i < aLi.length; i++){
aLi[i].onclick = function (){
alert(i);
};
}
</script>
</body>
运行之后发现无论点哪个标签,弹出的都是最后一个标签的index
这是因为 for 循环的里面 var 定义的变量 i 自动提升为全局变量,等同于下面的代码
<script type="text/javascript">
var oUl = document.getElementById("ul");
var aLi = oUl.getElementsByTagName("li");
var i;
for(i = 0; i < aLi.length; i++){
aLi[i].onclick = function (){
alert(i);
};
}
</script>
这时候 alert(i) 里面的i还没有值,当用户调用 onclick 的匿名函数时,需要对i求值
解析程序首先会在事件处理程序内部查找,但 i 没有定义。然后,又到方法外部去查找,此时有定义,但此时的i已经循环完毕,因此,无论点哪个标签,弹出的都是最后一个标签的index。
有以下几种方法解决:
立即调用的函数表达式(IIFE);不懂的可以看看我之前写的 点击这里
<body>
<ul id="ul">
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
<li>5555</li>
</ul>
<script type="text/javascript">
var oul = document.getElementById("ul");
var ali = oul.getElementsByTagName("li");
for(var i = 0; i < ali.length; i++){
(function(i){ //这里的i类似形参
ali[i].onclick = function (){
alert(i);
}
})(i); //这里的i类似实参
}
</script>
</body>
将变量 i 保存给在每个段落对象(li)上
<body>
<ul id="ul">
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
<li>5555</li>
</ul>
<script type="text/javascript">
var oul = document.getElementById("ul");
var ali = oul.getElementsByTagName("li");
for(var i = 0; i < ali.length; i++){
ali[i].i = i;
ali[i].onclick = function (){
alert(this.i);
}
}
</script>
</body>
将变量 i 保存在匿名函数自身
<body>
<ul id="ul">
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
<li>5555</li>
</ul>
<script type="text/javascript">
var oul = document.getElementById("ul");
var ali = oul.getElementsByTagName("li");
for(var i = 0; i < ali.length; i++){
(ali[i].onclick = function (){
alert(arguments.callee.i);
}).i = i
}
</script>
</body>
还有一种使用ES6新语法 let 关键字 由于是新语法 各浏览器支持不同
<body>
<ul id="ul">
<li>1111</li>
<li>2222</li>
<li>3333</li>
<li>4444</li>
<li>5555</li>
</ul>
<script type="text/javascript">
var oul = document.getElementById("ul");
var ali = oul.getElementsByTagName("li");
for(let i = 0; i < ali.length; i++){
ali[i].onclick = function (){
alert(i);
}
}
</script>
</body>
关于let,我一直似懂非懂,后来在阮一峰老师的《ECMAScript 6 入门》这本书上找到了答案
使用let,声明的变量仅在块级作用域内有效,当前的 i 只在本轮循环有效,所以每次都是一个新的变量
可能你会问,如果每一轮循环的变量 i 都是重新声明的,那它怎么知道上一轮循环的值,从而计算出本轮循环的值
这是因为Javascript引擎内部会记住上一轮循环的值,初始化本轮的变量 i 时,就在上一轮循环的基础上进行计算。
题外话
for循环还有一个特别之处,就是循环语句部分是一个父作用域,而循环体内部则是一个单独的子作用域
for (let i = 0; i < 3; i++) {
let i = "abc" ;
console.log(i);
}
// abc
// abc
// abc
上面的代码输出了3次 abc,这表明函数内部的变量 i 和外部的变量 i 是分离的