我从未使用addEventListener()
,但由于我生成内容的方式,我无法为每个<div>
编写我想要的HTML等效项。相当于:
<div onmousedown="jsItems[someId].toggleImage(someGallery, someIndex);"></div>
我一直在尝试的是:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
}
}
}
i
从0到1计数,而j
从0到2计数(在这种情况下对于i
都是),i
代表someGallery
, j
代表someIndex
,我可以在上面的代码中使用someId
访问this.id
(或self.id
的{{1}}函数内部addEventListener
。
问题是虽然点击其中一个“按钮”(<div>
s)会触发:
JsTree.prototype.toggleImage = function(inGallery, inIndex) {
alert(this.id+", "+inGallery+", "+inIndex);
}
无论点击哪个按钮,它始终会发出“8,2,3”警报。 “8”是正确的,但我不知道为什么“2”或“3”被警告。它们似乎只比i
和j
计数多1(通过尝试j < this.jsGalleries[i].buttons.length-1
验证“8,2,2”)。
修改:someId
,someGallery
和someIndex
不是真正的变量,它们是我试图解释问题的垃圾。
答案 0 :(得分:3)
这是一个经典的JS错误。问题是i
和j
的值未在任何函数范围内捕获,并且您的事件处理程序是异步的。这意味着当您的事件处理程序运行时,两个for
循环都已运行完成,因此i == this.jsGalleries.length
和j === this.jsGalleries[this.jsGalleries.length - 1].buttons.length
。
尝试以下其中一项:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
(function(self, innerI, innerJ){
var galleryEl = self.jsGalleries[innerI].buttons[innerJ];
galleryEl.addEventListener("mousedown", function() {
self.toggleImage(innerI, innerJ);
});
})(this, i, j);
}
}
}
或者可能更清楚:
JsTree.prototype.addGalleries = function(inElements) {
// ...unrelated code here removed for StackOverflow...
var addHandler = function(self, i, j){
self.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(i, j);
});
};
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
addHandler(this, i, j);
}
}
}
答案 1 :(得分:1)
addEventListener不是问题。这是一个常见的错误。为了理解发生了什么,我必须解释闭包是如何工作的。
当你有一个循环和一个函数时:
var i = 5;
while(i--){
setTimeout(function(){
console.log(i);
}, 100);
}
每个函数都被赋予对变量i
的引用。这意味着他们在您定义它们时不会保留i
的值。我再次重申,每个函数都引用同一个变量i
,而不是它在声明函数时的值。在上面的示例中,所有setTimeout都是异步定义的。匿名函数全部以100毫秒触发,每个函数在运行函数时记录i
中的值。在我的例子中,对于所有函数,该值将为-1。
有两种方法可以解决这个问题。我先向您展示一个简单的方法:
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
self.gallery = {i: i, j: j};
this.jsGalleries[i].buttons[j].addEventListener("mousedown", function() {
self.toggleImage(self.gallery.i, self.gallery.j);
});
}
}
在这里,您将值存储在实际的DOM元素上。这些值等于循环运行时的值,因此事件侦听器会获取正确的值。请注意,我将值嵌套在名为gallery的对象中。我这样做是为了命名空间。将值存储在DOM中的元素上并不是一个好主意,以防浏览器最终实现具有相同名称的属性。我觉得画廊很安全。
另一种选择,也许是解决这个问题的最佳做法,就是使用闭包技术。
for (var i = 0; i < this.jsGalleries.length; i++) {
for (var j = 0; j < this.jsGalleries[i].buttons.length; j++) {
var self = this;
this.jsGalleries[i].buttons[j].addEventListener("mousedown", (function closure(self, i, j){
return function actualListener(){
self.toggleImage(i, j);
}
})(self, i, j));
}
}
在这种情况下,我们创建一个自执行函数(在我的例子中称为闭包),它在我们创建监听器时立即运行。让我再说一遍,这个函数在添加监听器的那一刻运行,而不是在它运行的时候运行。我们这样做的原因是我们可以传递我们想要保存的值以便稍后保存,在本例中为self,i和j。然后,当事件发生时,ACTUALLY运行的函数是内部函数(称为actualListener)。 actualListener在关闭函数运行时有一个存储在其闭包中的所有值的副本。