题目简述
编写十个button,内容分别是1到10,点击其中的一个button则输出当前的序号,即点击1输出1。
解题思路
这里的十个button样式都是一样的,只是内容不同,显然使用js动态添加较为方便,很多人可能会这样写:
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.innerHTML = i;
doc.onclick = function(){
console.log(i);
};
document.body.appendChild(doc);
}
</script>
输出的效果是十个并排的按钮,内容分别是1到10,看似没有问题,但是点击之后,无论点击哪一个按钮,输出的总是11,这里就发生了闭包。
由于click不是立即执行的事件,当我们点击按钮的时候,上述代码的循环早已结束,我们输出的是最后的i值,那为什么是11呢?
根据for循环的逻辑,初始值是1,之后每次执行过后i+1,在执行到i=10的循环体之后,i再次+1,这时候变成了11,但是不能通过i<=10的判断,所以不再继续执行循环体,i最终为11。
为了解决这个问题,这里提供几个解题方法:
方法一,绑定id
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.id = doc.innerHTML = i;
doc.onclick = function(){
console.log(this.id);
};
document.body.appendChild(doc);
}
</script>
在赋值的同时把i同时给button动态添加一个id,id的值为i,虽然这样能实现我们的需求,但是很显然这不是最好的方式,这样的id是无意义的,并且很容易重复,所以如果使用绑定属性的方法,还是推荐大家使用下面的这个方法,使用dataset。
方法二,使用dataset新特性
dataset是HTML5里面的新特性,它叫自定义属性对象。使用方式也很简单:
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.dataset.value = doc.innerHTML = i;
doc.onclick = function(){
console.log(this.dataset.value);
};
document.body.appendChild(doc);
}
</script>
添加dataset属性时只需要doc.dataset.value即可,这里的value是自定义属性名,其在html里面生成的代码样式为:
<button data-value="1">1</button>
获取它的属性值也是直接doc.dataset.value即可获取。
方法三,使用bind
<script type="text/javascript">
for (var i = 1; i <= 10; i++) {
var doc = document.createElement('button');
doc.innerHTML = i;
doc.onclick = (function(){
console.log(this.toString());
}).bind(i);//绑定i值,同时this指向i
document.body.appendChild(doc);
}
</script>
我们知道,上述闭包的发生是因为i值的变化,那我们可以像个办法来组织它的变化。
使用bind函数可以使当前对象或函数绑定对应的值或对象并且将this指向它,这样的话每个button的点击事件就会被绑定i,这个i是当前的一次循环,但是我们使用bind把它和当前的button绑定住了,虽然大体的i在变化,但是每个button绑定的i不会在变化,所以这里我们可以输出当前的button序号。
很显然,这是最简单也是最好的办法。