簡體   English   中英

JavaScript addEventListener()無法正常工作

[英]JavaScript addEventListener() not working as expected

我從未使用過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 ,我可以在上面的代碼中(或與self.id someId使用this.id訪問self.idaddEventListener的函數中)。

問題是,盡管單擊這些“按鈕”( <div> )之一確實會觸發:

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

無論單擊哪個按鈕,它始終會發出“ 8、2、3”警報。 “ 8”是正確的,但我不知道為什么會警告“ 2”或“ 3”。 它們似乎僅比ij計數多1(通過嘗試j < this.jsGalleries[i].buttons.length-1來警告“ j < this.jsGalleries[i].buttons.length-1 ”)。

編輯someIdsomeGallerysomeIndex不是真正的變量,它們是我試圖解釋問題的垃圾someIndex

這是經典的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);
    }
  }
}

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));
    }
}

在這種情況下,我們創建一個自我執行函數(在我的示例中稱為closure),該函數在創建偵聽器時立即運行。 讓我再次聲明一下,此函數在添加偵聽器的那一刻開始運行,而不是在運行時運行。 這樣做的原因是,這樣我們就可以傳入想要保存以供以后使用的值,在這種情況下為self,i和j。 然后,當事件發生時,實際運行的函數是內部函數(稱為ActualListener)。 ActualListener具有運行閉包函數時存儲在其閉包中的所有值的副本。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM