鼠标点击li元素,弹出各自索引值

来源:互联网 发布:郭正亮 知乎 编辑:程序博客网 时间:2024/04/30 18:11

这篇博客我主要是把鼠标点击li元素,弹出各自索引值的几种方法进行总结。

问题:如下代码所示有好多li元素。实现的效果是当鼠标点击某个li元素时弹出自己的索引值(当然这里不是弹出它的innerHTML,这是凑巧索引值和它的innerHTML相等)

 <ul id="ul1">        <li>0</li>        <li>1</li>        <li>2</li>        <li>3</li>        <li>4</li>        <li>5</li>     </ul>

我们通常的做法是:

        var aLi=document.getElementById('ul1')        .getElementsByTagName('li');        for(var i=0;i<aLi.length;i++)        {             aLi[i].onclick=function(){                alert(i);             }        }

这是否如我们所愿呢,经过测试我们会发现,这段代码不管我们点击哪个都会弹出的是6.
这是为什么?
首先解释6是怎么来的,我们可以知道,这个循环的终止条件是i<aLi.length。也就是i不再<6。条件首次成立i 的值是6。因此,输出显示的是循环结束时i的最终值。

这里引入一个更深入的问题,代码中到底有什么缺陷导致它的行为同语义所暗示的不一致呢?

缺陷是我们试图假设循环中的每个迭代在运行时都会给自己“捕获”一个i的副本,但是根据作用域的工作原理,实际情况是尽管循环中的6个函数是在各个迭代中分别定义的,但是他们都被封闭在一个共享的全局作用域中,因此实际上只有一个i。

这样说的话,当然所有函数共享一个i的引用

我们可以通过声明并立即执行函数表达式来创建作用域。

var aLi=document.getElementById('ul1')        .getElementsByTagName('li');        for(var i=0;i<aLi.length;i++)        {            (function(){                var j=i;                 aLi[j].onclick=function(){                    alert(j);                 }            })();        }

当然我们也可以对这段代码进行改进

var aLi=document.getElementById('ul1')        .getElementsByTagName('li');        for(var i=0;i<aLi.length;i++)        {            (function(j){                 aLi[j].onclick=function(){                    alert(j);                 }            })(i);        }

当然,这些IIFE也不过就是函数,因此我们可以将i传递进去,至于参数的话名字随便起都行。

在迭代内使用IIFE会为每一个迭代都生成一个新的作用域,使得点击函数可以将新的作用域封闭在每个迭代内部,每个迭代中都会含有一个具有正确值的变量供我们访问。

考虑我们对前面的问题的解决方案的分析。我们是在每次迭代时都创建了一个新的作用域,换句话说,每次迭代我们都需要一个块作用域。我们可以使用let劫持块作用域。

var aLi=document.getElementById('ul1')        .getElementsByTagName('li');        for(var i=0;i<aLi.length;i++)        {           let j=i;//闭包的块作用域             aLi[j].onclick=function(){                alert(j);             }        }

我们可以进行代码优化

var aLi=document.getElementById('ul1')        .getElementsByTagName('li');        for(let i=0;i<aLi.length;i++)        {             aLi[i].onclick=function(){                alert(i);             }        }

for循环头部的let声明还会有一个特殊的行为。这个行为指出变量在循环过程中不止被声明一次,每次迭代都会声明。随后的每个迭代都会用上一个迭代结束时的值来初始化这个变量。

很酷是吧?块作用域和闭包联手,天下无敌。

最后我们再说一个方法

var  aLi=document.getElementById('ul1').getElementsByTagName('li');        for(var i=0;i<aLi.length;i++)        {             aLi[i].index=i;             aLi[i].onclick=function(){                alert(this.index);             }        }
1 0
原创粉丝点击