先来看一段代码:
<div class="box">
<ul>
<li>
<img src="images/4.jpg" alt="">
</li>
<li>
<img src="images/9.jpg" alt="">
</li>
<li>
<img src="images/11.jpg" alt="">
</li>
</ul>
</div>
var lis = document.querySelector('.box').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
//console.log(i);
console.log(lis[i]);
}
}
按照我们书写的逻辑,应该点击哪个li标签,就在控制台打印对应的标签,但是不论我们点击哪一个标签,控制台都打印undefined。
我们把console.log(lis[i]); 注释掉,换成 console.log(i); 发现:不论我们点击哪一个标签,控制台都打印3。这是为什么呢?
分析一下代码执行过程:
因为JS中点击事件为异步函数,JS会先执行那些非异步函数(同步函数)。也就是说,for循环会先循环三次,分别给第0,1,2个li标签注册了一个onlick事件。第四遍循环开始时i=3,不符合i<3的条件,因此终止循环。
注意: 此时只是注册了一个空事件,并不会执行点击事件,也就是并没有执行后面的function部分,而是会等待点击事件触发时才执行。也就是说,当我们点击按钮,触发点击事件的时候,for循环已经进行了三次,i值已经变成了3,且i值在整个for循环里都有效。
此时当我们点击某个标签时,触发了对应的事件执行程序,也就是执行后面function的内容console.log(i);,比如当点击第一个li标签时,执行的其实是:
lis[0].onclick = function () {
console.log(i);
}
而这个时候i值等于3,所以控制台打印的是3,而不论点击哪个标签,事件执行程序都是console.log(i);,这时i值一直等于3,所以不论我们点击哪一个标签,控制台都打印3。
同理,事件执行程序为console.log(lis[i]); 时,lis数组没有lis[3],数组溢出,所以打印undefined。
循环只是给每个标签注册了一个空的onclick事件,并不负责执行部分(因为一开始没有点击),当点击标签时执行对应的事件程序,这时候已经和for循环没关系了。
将lis[i]改为this:
var lis = document.querySelector('.box').querySelectorAll('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function () {
console.log(this);
}
}
this指向的是当前事件函数的调用者,用this的话,才能保证点击事件是指向当前标签的。比如当点击第一个li标签时,执行的其实是:
lis[0].onclick = function () {
console.log(this);
}
此时this指向的就是lis[0],从而可以进行相应的操作。
分别点击每个li标签,控制台打印情况如下: