<h2>点击以下三个li,输出分别是什么?</h2>
<ul>
<li>Item 0</li>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<script>
var lis = document.getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function (event) {
console.log('你点击了 ' + i);
}
}
</script>
第一个问题,点击三个li,console输出结果是什么?
聪明的你这时点击了三个li
“你点击了 3”
“你点击了 3”
“你点击了 3”
第二个问题,为什么输出一直是“3”呢?
此时,祭出我们的console.log大法:
<h2>点击以下三个li,输出分别是什么?</h2>
<ul>
<li>Item 0</li>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<script>
var lis = document.getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
console.log('添加第' + i + '个onclick listener');
lis[i].onclick = function (event) {
console.log(
'只有点击时才进入onclick这个function, 这时i已经变成了' + i);
console.log('你点击了 ' + i);
}
console.log('i这时变成了' + i);
}
console.log('i在for循环结束后变成了' + i);
</script>
第三个问题,如何修复这段代码,确保在点击Item 0, 1, 2的时候分别输出0, 1, 2?
第一种解法:利用closure对每一个 onclick event handler 建立一个其专属的 function,并在建立每个function的时候保存住 i 的值:
var lis = document.getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = (function (invokedi) {
console.log('现在建立对于Item' + invokedi + '的event handler');
return function uniqEventHandlerFori(event){
console.log(
'invokedi是在建立event handler时传入的,它的值是' +
invokedi);
console.log('你点击了 ' + invokedi);
}
}(i))
}
第二种解法:利用 Function.prototype.bind(thisArg, params…) 对每一个 onclick event handler 建立一个其专属的 function,并在建立每个function的时候保存住 i 的值:
var lis = document.getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function handlerToBind (event){
console.log(
'function handlerToBind(){...}.bind(i) --> ' +
' 建立了一个新的function,并且在这个函数里面,' +
'this === 运行bind(i)时i的值 === ' + this);
console.log('你点击了 ' + this);
}.bind(i);
}
第三种解法:event.target 拿到实际被点击的 li,然后利用 Array.prototype.indexOf(item) 去查找它的index。
var lis = document.getElementsByTagName('li');
console.log(
'通过document.getElementsByTagName返回的lis' +
'并没有返回真正的Array, 只是一个Array-like object. ' +
'我们需要把它构建成一个真正的Array才能在待会用indexOf方法. ' );
var lisArray = Array.prototype.slice.call(lis);
console.log('现在你可以愉快的使用lisArray.indexOf(item)了');
for (var i = 0; i < lis.length; i++) {
lis[i].onclick = function handlerToBind (event){
console.log(
'event.target 就是被点击的 DOM Element: ' +
event.target.innerHTML +
'. 它存在于lis和lisArray中');
console.log('你点击了 ' + lisArray.indexOf(event.target));
};
}
图片有点大,可能有点不清晰,我把文字描述写出来:
通过document.getElementsByTagName返回的lis并没有返回真正的Array, 只是一个Array-like object. 我们需要把它构建成一个真正的Array才能在待会用indexOf方法.
index.html:27 现在你可以愉快的使用lisArray.indexOf(item)了
index.html:33 event.target 就是被点击的 DOM Element: Item 0. 它存在于lis和lisArray中
index.html:38 你点击了 0
index.html:33 event.target 就是被点击的 DOM Element: Item 1. 它存在于lis和lisArray中
index.html:38 你点击了 1
index.html:33 event.target 就是被点击的 DOM Element: Item 2. 它存在于lis和lisArray中
index.html:38 你点击了 2
第四种解法:使用ES6的 let,把 i 限定在block level里面
var lis = document.getElementsByTagName('li');
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function (event) {
console.log('你点击了 ' + i);
}
}
第五种解法:使用立即执行函数,产生闭包。
var lis = document.getElementsByTagName('li');
for (var i = 0; i < lis.length; i++) {
(function(i) {
lis[i].onclick = function(event) {
console.log('你点击了 ' + i);
}
})(i)
}
第六种解法:点击获取文本
document.addEventListener('click', function(event) {
if (event.target.tagName == 'LI') {
console.log(event.target.innerHTML.replace("Item ", ""));
}
}, false);
PS:(⊙⊙!)
赵本山: 南脖儿万,说1+1在什么情况下等于3?
范 伟: 1+1在什么情况下也不等于3!
赵本山: 错!媳妇儿回答,
高秀敏: 在算错的情况下等于3。
赵本山: 错,看见没,算错的情况下等于3!
范 伟: 你算错~~~ 你算错那还等于6呢那还!
//手动狗头滑稽