2

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

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

为此,我有以下代码:

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 软件。)

4

3 回答 3

2

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

你可能需要类似的东西:

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

有关更详细的说明,请参阅JavaScript 闭包如何工作?.

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

button.addEventListener('click', function() {
    return toggleCollapsedPlugin(this.parentNode.id);
}, false);
于 2012-07-12T13:47:04.680 回答
1

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

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

代替

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

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

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

于 2012-07-12T13:46:48.627 回答
0

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

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

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

function(b) {
  var buttonParent=b.parentNode.id;
  b.addEventListener(...)
} (button);
于 2012-07-12T14:01:50.700 回答