javascript - 点击ul下面的li,弹出当前li的index

这个是之前面试的一道题,当时给的答案是类似下面这种:

var nodes = document.getElementsByTagName("li");
for(i = 0;i<nodes.length;i+= 1){
        nodes[i].onclick = (function(i){
                  return function() {
                     console.log(i);
                  } 
        })(i);
}

然后回来网上搜了搜,结果发现答案基本就是这一种,当时那个面试官对我这答案感觉不满意,问我还有别的方法实现么,我说不知道= =

这里问问大家,要是你,你会怎么回答这到问题呢?

阅读 12.3k
9 个回答

方式1. 使用闭包。

var ul = document.getElementsByTagName("ul")[0];  
var list = ul.getElementsByTagName("li");  

function foo(){  
    for(var i = 0, len = list.length; i < len; i++){  
        var that = list[i];  
        list[i].onclick = (function(k){  
            var info = that.innerHTML;  
            return function(){  
                alert(k + "----" + info);  
            };  
        })(i);  
    }  
}  

foo();  

方式2.事件代理

var ul = document.querySelector('ul');  
var list = document.querySelectorAll('ul li');  
  
ul.addEventListener('click', function(ev){  
    var ev = ev || window.event;  
    var target = ev.target || ev.srcElemnt;  
  
    for(var i = 0, len = list.length; i < len; i++){  
        if(list[i] == target){  
            alert(i + "----" + target.innerHTML);  
        }  
    }  
});  

方式3. 引入jQuery,使用其中的on或delegate进行事件绑定(它们都有事件代理的特性)

// delegate方法
$("ul").delegate("li", "click", function(){  
    var index = $(this).index();  
    var info = $(this).html();  
    alert(index + "----" + info);  
});  
 // on方法
$("ul").on("click", "li", function(){  
    var index = $(this).index();  
    var info = $(this).html();  
    alert(index + "----" + info);  
});  

方式4. 使用ES6中的新特性let来声明变量
用let来声明的变量将具有块级作用域,很明显可以达到要求,不过需要注意的是得加个'use strict'(使用严格模式)才会生效。

var ul = document.getElementsByTagName("ul")[0];  
var list = ul.getElementsByTagName("li");  
  
function foo(){'use strict'  
    for(let i = 0, len = list.length; i < len; i++){  
        list[i].onclick = function(){  
            alert(i + "----" + this.innerHTML);  
        }  
    }  
}  
foo();  

事件代理?

<ul>
    <li id="li1">1</li>
    <li id="li2">2</li>
    <li id="li3">3</li>
    <li id="li4">4</li>
</ul>
var nodes = document.getElementsByTagName("ul");
    nodes[0].addEventListener('click', function(e){
        console.log(e.target.id.replace("li", ''));
    });
新手上路,请多包涵

在这堆li元素的父元素上挂事件可能是更好的实现方式。

首先你得东西有一个很大的问题,就是你获取的是页面上所有的li,所以console.log(i)输出的是在所有li中,当前的是第几个。

至于要输出index,几种写法:
不过现在好像更推荐addEventListener来添加事件,然后选择器用的更精确一点。

  1. 闭包,和你的一样。

  2. let来声明i

  3. 或者通过js的方法获取当前liindex

for(let node of nodes) {
    node.addEventListener("click", function(){ 
        console.log(Array.prototype.indexOf.call(this.parentNode
    .children, this)); 
    })
}
  • 这个问题的考察点是闭包,你的答案已经做到了。

  • 还可以改进的就是绑定事件的方式,做到兼容旧版本的IE。

  • 还可能会有要求输出当前 UL中的li次序。

  • 如果li很多,每个都绑定事件太占内存了,需要用到事件代理。但是使用代理后每次点击都遍历一遍太浪费,可以第一次遍历后缓存起来(像jQuery一样),只要DOM结构不改变,下次点击可以直接返回缓存中的li了。

闭包太占用内存,如果考虑优化效果的话,可以试试利用自定义属性在for循环里边的第一句加上一个nodes[i].index = i;这样的话不会形成私有作用域内存不释放。还能利用自定义属性找到正确的索引。

    <ul class="menu_list">
        <li>demo1</li>
        <li>demo2</li>
        <li>demo3</li>
    </ul>
    <script type="text/javascript">
    // 不想 用 闭包 的 话 不是 还有个 最常用的 吗 像这样的 以前 楼主 不经常 看到 吗、、、、、、
         // 或者 用 es6 let 的 块级 作用域
    for (var i = 0,oLi = document.querySelectorAll('.menu_list > li'),l = oLi.length;i < l;i++){
        oLi[i].index = i;
        oLi[i].onclick = function(){
            alert(~~this.index);
        };
    }
      </script>

这题是考性能优化的吧?
用事件委托来答题应该好点

做vue时,tabel里每一行里都有button,点击button时要知道button所在tr的index。
我也用过for循环里给每个tr加onclik的方法,不过即使我先调用它:闭包获取index();editRouter(),editRouter里得到的值并不是最新获取的index。
解决方法如下:

<tr class="tableBody" v-for="items in tableData">
                <td v-for="(item,index) in items" v-if="['page','label','dispStatus','version','validation','display'].indexOf(index) === -1">
                    <p>{{item}}</p>
                    <!-- <p v-if="index == 'constCondition'">{{JSON.parse(item.constCondition).maxLength}}</p> -->
                </td>
                <td :formId="items.id">
                    <el-button @click="returnTrIndex();editRouter()" :formId="items.id" class="editDeleteBtn" icon="el-icon-edit" circle></el-button>
                    <el-button v-if="items.dispStatus ==1" @click="returnTrIndex();deleteTr()" :formId="items.id" class="editDeleteBtn" type="danger" icon="el-icon-delete" circle></el-button>
                </td>
</tr>

method:

returnTrIndex() {
            this.trIndex = event.target.parentNode.getAttribute('formId');
            for(var i=0;i<this.tableData.length;i++){
                if (this.tableData[i].id == this.trIndex){
                   return this.trIndex = i;
                }
            }
 }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏