Javascript事件处理程序始终被添加到数组中的最后一个对象

时间:2012-07-12 13:40:06

标签: javascript javascript-events addeventlistener vbulletin

我正在尝试在“切换折叠”按钮(实际上是图像)上执行两项任务:

  1. 如果页面的一部分已加载折叠(来自cookie),则将“折叠”类添加到祖先标记。
  2. 添加一个事件处理程序,以便在单击按钮时切换“折叠”类的存在。
  3. 为此,我有以下代码:

    function toggleCollapsedPlugin(sender, collapseState) {
        // do something
        // expects 1 OR 2 arguments
    }
    
    function initCollapsedPlugin() {
        var forumcats = document.getElementById('forums').getElementsByClassName('forumbit_nopost L1');
        var button;
        var buttonParent;
        for (var i = 0; i < forumcats.length; i++) {
            button = forumcats[i].getElementsByTagName('a')[1].getElementsByTagName('img')[0]; // will always exist
            buttonParent = button.parentNode.id; // will never be empty
            if (button.src.indexOf('_collapsed') >= 0) {
                toggleCollapsedPlugin(button.parentNode.id, true);
            }
            button.addEventListener('click', function () { toggleCollapsedPlugin(buttonParent); }, false);
        }
    }
    

    当我通过initCollapsedPlugin()逐步浏览Chrome DevTools中的第一项时,一切似乎都没问题。当我稍后点击其中一张图片以致电toggleCollapsedPlugin(sender)时,sender始终等于buttonParent最后项的forumcats。有人可以解释为什么以及如何解决这个问题吗?

    (如果它有助于任何人提供答案,那么这就是vBulletin4软件。)

3 个答案:

答案 0 :(得分:2)

这是JavaScript闭包的经典“陷阱”。基本上,您的buttonParent变量在每次迭代中被覆盖,并且您传递给addEventListener 引用的匿名函数但不复制此变量。

你可能需要类似的东西:

button.addEventListener('click', (function (parent) {
    return function() {
        toggleCollapsedPlugin(parent);
    };
})(buttonParent), false);

有关更详细的说明,请参阅How do JavaScript closures work?

然而,在你的情况下,你可以简单地完全摆脱闭合变量,并在调用匿名函数时找到按钮的父项。这是有效的,因为回调函数内部的this引用了事件在其上注册的元素,是该迭代中button引用的元素。

button.addEventListener('click', function() {
    return toggleCollapsedPlugin(this.parentNode.id);
}, false);

答案 1 :(得分:1)

您正在声明buttonParent,将其分配给事件调用,但随后在循环中更改它,直到最终它引用最后一项。

我认为所有事件都会指向同一个参考文献。

替换

button.addEventListener('click', function () { toggleCollapsedPlugin(buttonParent); }, false);

button.addEventListener('click', function () { toggleCollapsedPlugin(this.parentNode.Id); }, false);

按钮对象上的事件内的“this”将是按钮本身

答案 2 :(得分:0)

您获取buttonParent的最后一个值,因为每个事件处理程序都引用相同的变量。 buttonParent被捕获为函数关闭局部变量的一部分。

当调用事件处理程序时,buttonParent的值已被for循环设置为最后一个按钮的父级。

这就是为什么通常不应该在循环中声明函数。通常的解决方案是使用函数调用将值传递给事件处理程序。您不是直接调用addEventListener,而是调用一个函数,传递要添加侦听器的按钮。调用该函数的行为将变量引用复制到函数参数中,因此每个事件侦听器都有一个单独的副本。

function(b) {
  var buttonParent=b.parentNode.id;
  b.addEventListener(...)
} (button);