ul
中有li
,点击li
,获得对应的序号Happy coding…
作为一个前端开发者,一定会遇到这样的一个问题:
一个
中里面嵌套了好多空的
标签,点击
弹出它的序号(HTML不可以修改)。即点击第一个
标签时弹出
0
,点击第二个标签时弹出
1
。HTML的结构如下:
<ul id="zhuo">
<li>1li>
<li>2li>
<li>3li>
ul>
我们一看到这样的需求及HTML结构时,第一时间就会想着用for + onclick + console.log
来完成这个事情:
var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')
for(var i = 0, len = li.length; i < len; ++i){
li[i].onclick = function () {
console.log(i) // 写到这里,心中暗爽:不就这么简单吗?那你这坑就踩得稳稳的,一测试发现
}
}
// 无论你点击哪一个标签都会输出:3
// 这是为什么呢?
// 下面分析一下代码。
// console.log(i) // 假装没有被注释
for
循环时,我们给每一个li
注册了一个click事件,每一个li
被点击的时候应该弹出它的序号i
;li
的时候,for
循环早已经执行完,已经给每个li
注册了事件,事件的绑定是异步操作!for
循环已经遍历完,i
的值已经变成li.length
(问题就出现在这里)li
时,弹出的是3
,而不是它对应的序号i
本应在循环结束后就被销毁,但,这里并没有!没有块级作用域,那么我们给它伪装一个不就行了?那么问题来了:{}
并不能代表一个作用域,那么用什么来解决这个问题呢?
相信聪明的你已经想到了,用一等公民的function
,函数拥有自己的作用域;
var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')
for (var i = 0, len = li.length; i < len; ++i) {
(function (j) {// 什么?你问我这个是什么意思?
// 这是一个闭包函数(IIFE),立即执行函数
// 接收i的值保存在j中,这个j会一直保存在这个函数作用域
li[j].onclick = function () {
console.log(j)
}
})(i)// 这里可以看成函数的入口,传入i
}
什么?你说这个太烧脑?那好吧,我讲演示个简单的:
var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')
for (var i = 0, len = li.length; i < len; ++i) {
li[i].index = i // 因为这个li是一个dom对象,所以我们可以在它上面添加属性
li[i].onclick = function () {
var index = this.index // 这里的this指向li[i],因此我们可以从上面取到之前的index属性值
console.log(index)
}
}
// 终于写完了....
纳尼?你说,this
的指向太难理解?你怎么就这么难伺候叻。。。
好吧,我再和你说一个“爸爸给儿子帮忙”的做法:
li
)小时候,想带个玩具(index
)去跟小朋友玩(炫耀);ul
)/大舅(div
)/爷爷(body
)这一整个流程就是,DOM中的冒泡机制(1,2,3)与捕获机制(4,5,6),这也就是我们接下来要说的委托事件原理。
var ul = document.getElementById('zhuo')
ul.onclick = function(event){
event = event || window.event // 额...理解不了的话,你当这个是一个DOM对象
target = event.target // 获得点击的最底层DOM
if(target.nodeName === 'LI'){// 判断这个DOM节点名字是不是li,
console.log(target.innerHTML)// 好了,被你发现了;这里不是弹出序号,是内容。
}
}
// 聪明的同学一定看出来了,这样的写法更简洁,不需要for循环,不需要给每个li绑定click事件
// 这样做的好处多多
// 诶诶诶,你别走啊,都看到这里了,接着看下去呗。
// 下面的办法更简单
好了,看到这里,你会疑惑为什么是ES6之前没有,难道现在就有了?
没错,在前端开发的历(ku)史(bi)长(ri)河(zi)中,开发者们越来越觉得不对劲,于是就在ES6加入了牛逼哄哄的let
;
let
到底是何方神圣,为什么博主在这里说它?他?她?牛逼呢?
let
允许你声明一个作用域被限制在块级中的变量、语句或者表达式。与var
关键字不同的是,它声明的变量只能是全局或者整个函数块的。
也就是说,用这个let
,我们就可以很nice地解决上面提到的问题了
var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')
for(let i = 0, len = li.length; i < len; ++i){
li[i].onclick = function () {
console.log(i) // 当当当,是不是觉得So easy?
}
}
// 什么?你的开发环境不支持ES6的语法?
// 告辞!打扰了
// 我不跟chrome都没升级到最新版的人交朋友
// 再见!
都看完了,也不评论一下,点个赞吗?
觉得写得不错的同学,可以到我的codepen逛逛噢。