"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' />