我正在尝试在“切换折叠”按钮(实际上是图像)上执行两项任务:
为此,我有以下代码:
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软件。)
答案 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);