"click image to display its id" 及其陷阱
"click image to display its id" and its pitfall
这个标题看起来很简单,但这并不是我想问的。我只是想不出更好的标题,如果您在阅读以下内容后有任何疑问,请发表评论,我会更新标题。
使用jQuery,做到这一点相当容易,因为click()
传递了this
参数,
$('img').click(function(){console.log($(this).attr('id')})
我尝试使用 vanilla javascript 来做到这一点,但遇到了一些陷阱
imgs = document.getElementsByTagName('img');
l = imgs.length;
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){console.log(imgs[i].id)});
}
这行不通,因为每个听众都会尝试 console.log(imgs[l].id)
。
我已经找到解决方法:
imgs = document.getElementsByTagName('img');
l = imgs.length;
listener_generator = function(id){
var s = function(){
console.log(id);
};
return s;
};
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',listener_generator(imgs[i].id));
}
出于学习的目的,我记得以前看过一些javascript的书,除了上面的方法,还有其他方法可以做到这一点,但我现在找不到了。我的问题是,还有其他方法可以做到这一点吗?哪个是最佳做法?
这是一个经典的闭包问题,您可能无意中解决了这个问题。
这段代码的问题:
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){console.log(imgs[i].id)});
}
基本上就是console.log(imgs[i].id)
不是立即求值,是在触发事件监听器的时候求值。这意味着您有很多引用 i
的表达式,但由于它们在事件侦听器触发之前不会执行,因此它们都具有完全相同的 i
值(它的最后一个值在 for 循环的末尾)。
为了解决这个问题,您需要 "close over" i
的值来排序捕获它的值 在 for 循环 而不是稍后当事件侦听器触发时。有多种方法可以做到这一点——我的首选方法是创建一个匿名函数,为 for 循环的每次迭代执行,如下所示:
for (i=0;i<l;i++) {
(function(_i){
imgs[_i].addEventListener('click',function(){console.log(imgs[_i].id)});
})(i);
}
您基本上是通过创建闭包无意中解决了这个问题,只是您使用的是命名函数而不是匿名函数,但结果是一样的。在运行时,for 循环调用一个函数,该函数 "captures" 在闭包中 i
的值,而不是将其推迟到事件侦听器触发。
Here is an excellent post with more detail about how closures work.
如果您在点击事件中将 img[i]
替换为 this
以引用当前 img,那么第一个代码应该可以工作:
imgs = document.getElementsByTagName('img');
l = imgs.length;
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){console.log(imgs[i].id)});
}
希望对您有所帮助。
imgs = document.getElementsByTagName('img');
l = imgs.length;
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){alert(this.id)});
}
<img src='http://tny.im/3Qp' id='img_1' />
<img src='http://tny.im/3Qp' id='img_2' />
<img src='http://tny.im/3Qp' id='img_3' />
<img src='http://tny.im/3Qp' id='img_4' />
这个标题看起来很简单,但这并不是我想问的。我只是想不出更好的标题,如果您在阅读以下内容后有任何疑问,请发表评论,我会更新标题。
使用jQuery,做到这一点相当容易,因为click()
传递了this
参数,
$('img').click(function(){console.log($(this).attr('id')})
我尝试使用 vanilla javascript 来做到这一点,但遇到了一些陷阱
imgs = document.getElementsByTagName('img');
l = imgs.length;
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){console.log(imgs[i].id)});
}
这行不通,因为每个听众都会尝试 console.log(imgs[l].id)
。
我已经找到解决方法:
imgs = document.getElementsByTagName('img');
l = imgs.length;
listener_generator = function(id){
var s = function(){
console.log(id);
};
return s;
};
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',listener_generator(imgs[i].id));
}
出于学习的目的,我记得以前看过一些javascript的书,除了上面的方法,还有其他方法可以做到这一点,但我现在找不到了。我的问题是,还有其他方法可以做到这一点吗?哪个是最佳做法?
这是一个经典的闭包问题,您可能无意中解决了这个问题。
这段代码的问题:
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){console.log(imgs[i].id)});
}
基本上就是console.log(imgs[i].id)
不是立即求值,是在触发事件监听器的时候求值。这意味着您有很多引用 i
的表达式,但由于它们在事件侦听器触发之前不会执行,因此它们都具有完全相同的 i
值(它的最后一个值在 for 循环的末尾)。
为了解决这个问题,您需要 "close over" i
的值来排序捕获它的值 在 for 循环 而不是稍后当事件侦听器触发时。有多种方法可以做到这一点——我的首选方法是创建一个匿名函数,为 for 循环的每次迭代执行,如下所示:
for (i=0;i<l;i++) {
(function(_i){
imgs[_i].addEventListener('click',function(){console.log(imgs[_i].id)});
})(i);
}
您基本上是通过创建闭包无意中解决了这个问题,只是您使用的是命名函数而不是匿名函数,但结果是一样的。在运行时,for 循环调用一个函数,该函数 "captures" 在闭包中 i
的值,而不是将其推迟到事件侦听器触发。
Here is an excellent post with more detail about how closures work.
如果您在点击事件中将 img[i]
替换为 this
以引用当前 img,那么第一个代码应该可以工作:
imgs = document.getElementsByTagName('img');
l = imgs.length;
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){console.log(imgs[i].id)});
}
希望对您有所帮助。
imgs = document.getElementsByTagName('img');
l = imgs.length;
for (i=0;i<l;i++) {
imgs[i].addEventListener('click',function(){alert(this.id)});
}
<img src='http://tny.im/3Qp' id='img_1' />
<img src='http://tny.im/3Qp' id='img_2' />
<img src='http://tny.im/3Qp' id='img_3' />
<img src='http://tny.im/3Qp' id='img_4' />