JavaScript - 循环遍历所有a标签,给每个标签添加一个onclick事件

3
我有一列链接,指向图片,并且有一个js函数,当调用该函数时,将在页面上放置该图片。原本我正在为每个a标签添加inline onlick="showPic(this.getAttribute('href'))",但是我想分离出这个inline js。下面是在页面加载时为每个a标签添加onclick的函数:
function prepareLinks(){
var links = document.getElementsByTagName('a');
for(var i=0; i<links.length; i++){
    var thisLink = links[i];
    var source = thisLink.getAttribute('href'); 
    if(thisLink.getAttribute('class') == 'imgLink'){
        thisLink.onclick = function(){
            showPic(source);
            return false;
        }
    }
}
}

function showPic(source){
var placeholder = document.getElementById('placeholder');
placeholder.setAttribute('src',source);
}

window.onload = prepareLinks();

每次调用showPic时,source变量都是最后一张图片的href。如何让每个链接都具有正确的onclick事件?


1
不想使用jQuery,想让这成为一个真正的一行代码吗? - Kirk Woll
4
你的 window.onload 写法有误,它会把 prepareLinks() 的返回值赋给 windowload 事件。 - alex
@alex:好眼力,我甚至没有看到那个。 - Ruan Mendes
Kirk - 谢谢,但这需要普通的仅JS。 @alex - 谢谢,我已经在我刚发布的解决方案中解决了这个问题。 - tripRev
4个回答

4

JavaScript没有块级作用域,因此闭合变量最终将成为最后分配给它的值。您可以通过将其包装在另一个闭包中来解决这个问题:

function prepareLinks() {
    var links = document.getElementsByTagName('a');
    for(var i = 0; i < links.length; i++) {
        var thisLink = links[i];
        var source = thisLink.getAttribute('href'); 
        if(thisLink.getAttribute('class') == 'imgLink') {
            thisLink.onclick = (function(source) {
                return function() {
                    showPic(source);
                    return false;
                };
            })(source);
        }
    }
}

当然,你可以简化它并使用this:
function prepareLinks() {
    var links = document.getElementsByTagName('a');

    for(var i = 0; i < links.length; i++) {
        var thisLink = links[i];

        if(thisLink.getAttribute('class') == 'imgLink') {
            thisLink.onclick = function() {
                showPic(this.href);
                return false;
            };
        }
    }
}

我相信这会破坏与IE5或IE6的兼容性,但希望你不会关心它们 =)

1
@JuanMendes: 实际上,块级作用域确实可以解决这个问题。至于从处理程序中获取它-已添加 ;) - Ry-
我仍然不同意,我认为块作用域并不能解决这个问题,“source”与所有处理程序在同一块中定义。 - Ruan Mendes
@JuanMendes:那真的不重要。以C#为例,它使用块作用域来解决问题。 - Ry-
希望我更好地了解C#,我对这段代码还不是很理解。但是在那个例子中你必须添加一个新的变量j,只打印i会输出5... 对我来说,块作用域只意味着在该块之外无法访问该变量。块作用域变量可以引入闭包中... :p - Ruan Mendes
@JuanMendes:是的,C#的foreach循环就是这样。在任何范围内都是一半一半的。但它不是闭包 :) - Ry-
显示剩余2条评论

2

Minitech的回答应该可以解决你的问题,即source变量被所有onclick处理程序共享。

你的做法非常浪费,没有必要为每个链接设置单独的处理程序。此外,如果动态添加任何链接,则无法正常工作。事件委托是解决问题的方法。

function interceptLinks() {
   // Bad way to set onclick (use a library)
   document.onclick = function() {
     if (this.tagName.toUpperCase() != 'A' ) {
       return;
     }
     // Bad way to check if it contains a class (use a library)
     if (this.getAttribute('class') == 'imgLink') {
       showPic(this.getAttribute('href'));
       return false;
     }
   }
}

0

这是一个古老的问题,即循环内部的事件处理程序访问外部变量。

click事件发生时,您的source变量已经从作用域链中移除,并且此时由于迭代已经完成,它已被设置为最后一个href属性。

您需要通过以下两种方法之一来打破闭包。

最简单但不被许多浏览器支持的方法是使用let,它允许您使用块级作用域。

let source = thisLink.getAttribute('href'); 

jsFiddle。它在Firefox中可以工作,但在Chrome中不行。

到了2038年,当我们处理2038年问题并且所有浏览器都已经实现了ES6时,这将成为解决此问题的标准方法。

一种更难理解和实现的方法是使用诸如...的模式来打破闭包,以兼容所有浏览器。

thisLink.onclick = (function(src) {
  return function(){
            showPic(src);
            return false;
        }
})(source);

jsFiddle


1
“let” 还不是 EcmaScript 标准,它是 Mozilla 的 JavaScript。希望它能尽快被纳入 EcmaScript 标准。 - Ruan Mendes
@JuanMendes 它正在路上 - alex

0

感谢所有的回复。事实证明,我对问题的诊断有误,非常抱歉。实际上,在每次循环迭代中使用新变量和匿名函数来添加onclick是有效的(传递的href是正确的)。它之前没有起作用是因为我通过“imgLink”类获取了a标记,而当我删除内联onclick处理程序时,我已经从HTML中删除了该类(现在我通过父级ID获取它们)。此外,我需要使用“return!showPic(this.href);”来阻止在单击时正常跟随链接。

有效代码:

function showPic(source){
var placeholder = document.getElementById('placeholder');
placeholder.setAttribute('src',source);
return true;
}

function prepareLinks() {
if(!document.getElementById('imgLinks')){ return false; };
var galLinks = document.getElementById('imgLinks');
var links = galLinks.getElementsByTagName('a');
for(var i=0; i<links.length; i++) {
    var thisLink = links[i];
    thisLink.onclick = function() {
        return !showPic(this.href);
    };
}
}

window.onload = function(){
prepareLinks();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接