【问题标题】:JavaScript addEventListener() not working as expectedJavaScript addEventListener() 没有按预期工作
【发布时间】:2013-07-08 13:35:52
【问题描述】:

我从未使用过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 表示 someGalleryj 表示 someIndex,而 I可以在上面的代码中使用this.id 访问someId(或在addEventListener 的函数中使用self.id)。

问题在于,尽管点击这些“按钮”之一 (&lt;div&gt;s) 会触发:

JsTree.prototype.toggleImage = function(inGallery, inIndex) {
    alert(this.id+", "+inGallery+", "+inIndex);
}

无论单击哪个按钮,它都会始终提示“8、2、3”。 “8”是正确的,但我不知道为什么会提醒“2”或“3”。它们似乎比 ij 计数的数量多 1(通过尝试警告“8、2、2”的 j &lt; this.jsGalleries[i].buttons.length-1 进行验证)。

编辑someIdsomeGallerysomeIndex 不是真正的变量,它们是我为了解释问题而编造的垃圾。

【问题讨论】:

    标签: javascript addeventlistener


    【解决方案1】:

    这是一个典型的 JS 错误。问题是ij 的值没有在任何函数范围内被捕获,并且您的事件处理程序是异步的。这意味着当您的事件处理程序运行时,两个 for 循环都已运行完成,因此 i == this.jsGalleries.lengthj === 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);
        }
      }
    }
    

    【讨论】:

    • 谢谢,我想第二个对我来说会更容易理解
    【解决方案2】:

    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。然后,当事件发生时,实际运行的函数是内部函数(称为actualListener)。在闭包函数运行时,actualListener 拥有存储在其闭包中的所有值的副本。

    【讨论】:

    • 我现在明白这个问题了,谢谢。还是要花点时间理解解决方案,我再多看几遍,哈哈
    • 我正在添加另一种解决方案,这可能是您想要使用的解决方案。我会在一秒钟内把它分解在这里;-)
    • 函数末尾的(self, i, j) 看不懂}。你能谈谈吗?
    • function(a,b){ console.log(a,b) }(1,2)function blah(a,b){ console.log(a,b) }; blah(1,2) 相同。在第二种情况下,blah 等于 function(a,b){ console.log(a,b) }。使用括号告诉解释器执行命令。
    • 您不必用() 包围该函数,但人们这样做是为了让您知道该函数会立即被调用。 (function(){})(222) 等于 function(){}(222)
    猜你喜欢
    • 1970-01-01
    • 2017-04-08
    • 2017-03-29
    • 1970-01-01
    • 2016-06-19
    • 2020-11-25
    • 2016-11-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多