点击每个兄弟节点获取对应节点下标的六种方案详解

点击每个兄弟节点获取对应节点下标的五种方案详解

一、前言

在 DOM 节点中,或者在循环引用中,如何点击每个兄弟节点获取对应节点下标,比如 ul 下有 3个 li,要求实现点击每个 li 获取其对应的下标。这是一道面试题,也是项目中非常常见的功能,我在写上一篇《详解 JavaScript 中的闭包》的时候,有提到过这几个方法,但是没有详细说明,今天就给大家说说每个方法的实现原理。

二、示例

  • HTML 代码示例:
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
  • 可能出现的错误脚本:
<script>
var lis=document.getElementsByTagName('li');
for(var i=0;i<=lis.length-1;i++){
        lis[i].onclick = function () {
            console.log(i)
    };
}
</script>
  • 出现的现象:
    不管点击那个 li 都会打印出 3。
  • 原因:
    这是因为 for 循环中的作用域是对全局开放的,即在外部也可以访问或改变循环内的变量,以上脚本中,在全局环境下打印出 console.log(i),输出 3。而 onclick 是异步事件,即循环执行完,或者同步代码执行完,当触发点击的时候才执行该事件。请看以下脚本。
<script>
var lis=document.getElementsByTagName('li');
for(var i=0;i<=lis.length-1;i++){
     lis[i].onclick = function () {
            console.log(i)
    };
};
i = i+3;
console.log(i);//6 点击每个 li 打印出来的就是 6 。
//以上的执行顺序等同于
var i = 0;
i++;//执行了3遍;i =3;
i = i+3; //i =6;
lis[i].onclick = function () {
     console.log(i)// 6 因为该事件是异步函数,所以最后执行,此时 i 已经为6。
};
</script>
  • 解决思路
    所以由以上分析得出,要解决该问题,就需要用到一个外部环境访问不到的变量。

三、解决方案

1. 使用 let

//将以上循环脚本中的 var 改成 let 即可
for(let i=0;i<=lis.length-1;i++){
    lis[i].onclick = function () {
         console.log(i)
   };
}

解析:let 是 ES6 引进的新语法,之所以可以解决这个问题是因为 let 有块级作用域。想了解更多 let 特性,可查看《ES6 语法之 let 与 const》,块级作用域的特性使 let 将循环内的变量锁定了,外层无法访问,也无法改变。

2. 给 DOM 属性赋值

for(var i=0;i<=lis.length-1;i++){
    lis[i].id = i;
    lis[i].onclick = function () {
          console.log(this.id)
   };
}

解析:这个很好理解,for 循环在执行的时候,就将变化的变量 i 赋值给每个 li 对象的 id 属性(赋值后 li 对象就保存了这个属性),通过点击获取 this 对象(当前 li)的 id 属性,即可变相的获取到下标值。

3. 使用传参闭包1

for(var i=0;i<=lis.length-1;i++){
    lis[i].onclick = (function (m) {
        return function(){
          console.log(m)
       }
   })(i);
}

解析:赋值给 onclick 的事件是一个自执行的匿名函数,将循环变量 i 作为参数传递给匿名函数调用并保存起来,因为外部获取不到也改变不了形参 m ,所以当事件触发的时候获取的就是对应的下标。

4. 使用传参闭包2

for(var i=0;i<=lis.length-1;i++){
    (function(m){
        lis[i].onclick = function () {
            console.log(m)
        };
    })(i)
}

解析:在循环体内再创建一个函数,使用该函数的参数与循环变量当前的值绑定,无论该循环变量如何改变, 已绑定到该函数参数的值不会被外部改变。

5. 使用不传参闭包+变量关联

for(var i=0;i<=lis.length-1;i++){
    (function(){
        var id = i;
        lis[i].onclick = function () {
            console.log(id)
        };
    })()
}

解析:创建一个匿名函数自执行的词法作用域,把当前循环项内的 id 与事件回调绑定起来,并保存对应的 id 值,在打印的时候打印 id 值即可查找到对应的下标值。

6. 使用数组循坏

var lis = document.getElementsByTagName('li');
var item = Array.from(lis);
    item.map((item,i)=>{
        item.onclick=function () {
            console.log(i);
        }})

解析:使用 Array将集合转换为数组形式,再用 map 循坏,因为 map 循坏中都是局部变量,外部访问不到,所以不会被修改或受影响。

四、总结

其实看着很复杂,思路很简单,只要保证这个下标变量不被外部环境所影响即可。换句话说,它要有自己独立的局部作用域。而 for 循坏内不是块级作用域,其内部的变量会被外部所影响。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值