2016-10-03 49 views
0

我有一個JavaScript頁面,用於監聽一系列音頻文件中的某些故事。我使用爆米花庫爲腳註添加特定的時間戳,在音頻播放時突出顯示頁面文本中的文字。我有一個JavaScript對象AudioPlayer(本例中的實例稱爲ap),其中包含許多音頻元素和許多爆米花實例。第一次音頻元素具有'loadedmetadata',如果我有該項目的時間戳,我想創建爆米花腳註。但是,我不想爲給定的音頻項目多次運行此功能。也就是說,人們可以點擊一個項目並重新加載它,但我不想重新創建時間戳。出於這個原因,我寫了這樣的回調代碼時,我得到了一個給定的項目時間戳來運行:如何在回調期間從此刪除事件監聽器

// try to load any timestamps for this file 
this.loadTS = function(ann) { 
    var xhr = new XMLHttpRequest(); 
    xhr.open("GET", window.location.protocol+"//"+ 
        window.location.hostname+"/sitename/timestamps/"+ 
        window.location.pathname.substring(
        window.location.pathname.lastIndexOf('/')+1)+".json", 
      true); 
    xhr.onreadystatechange=function(){ 
    if(xhr.readyState==4 && xhr.status==200){ 
     console.log(xhr.responseText); 
     this.timestamps = JSON.parse(xhr.responseText); 
     for(var idx in this.timestamps){ 

     var stampX = function(){ 
      // this is an audio element, get it's index to 
      // do the stamping 
      var x = window.ap.audios.indexOf(this); 

      // need to remove this listner so it doesn't fire again!   
      this.removeEventListener('loadedmetadata',stampX); // <- fail :(
      // window.ap.audios[x] 
      // .removeEventListener('loadedmetadata',stampX); 
      // ^^ this failed too :(:(

      // stamp away! 
      window.ap.stampItem(window.ap.winIGTs[x], 
      window.ap.timestamps[x], window.ap.audios[x], 
       window.ap.popcorns[x]); 

     }; 

     this.audios[idx].addEventListener('loadedmetadata', stampX); 

     if(ann) 
      this.textIGTs[idx].setAttribute("class","igt existstamps"); 
     } 
    } else console.log(xhr.status); 
    }.bind(this); 
    xhr.send(); 
} 

但我在測試這個代碼,「stampX」是越來越再次調用發現如果音頻元素被重新加載,所以我唯一的結論是,removeEventListener在某種程度上沒有得到與下面的addEventListener相同的引用。

我發現這很難調試。我無法將變量傳遞給stampX,因爲需要事件偵聽器的函數引用(不是函數調用)。

無論如何,我很難找到正確的方法來寫這個,這樣我可以在第一次調用stampX時刪除eventListener。

回答

1

看起來像每次都重新創建stampX,因爲您在onreadystatechange函數中聲明瞭它。

這意味着您所做的每個addEventListener都會獲得不同的回調函數。

你需要做的是分離它的邏輯,把它移到外面,在詞法作用域上更高,例如聲明xhr對象的例子,甚至在loadTS減速之外。這樣,addEventListener和removeEventListener都將指向相同的函數。

0

編輯:看到上面的答案,我剛纔在下面寫到我接受爲正確的fistuks。我會離開這個,因爲它在問題的上下文中舉例說明了解決方案。


我有一個解決方案,但我仍然想知道更多關於爲什麼。

對於我來說,解決問題的辦法是將stampX移動到xhr.onreaystatechange的回調之外的window.ap對象名稱空間。

this.stampX = function(e) { 
    // this is an audio element, get it's index to do the stamping 
    var x = window.ap.audios.indexOf(e.target); 
    // need to remove this listner so it doesn't fire again! 
    this.audios[x].removeEventListener('loadedmetadata',this.stampX); 

    this.stampItem(this.winIGTs[x], this.timestamps[x], this.audios[x], 
     this.popcorns[x]); 

    }.bind(this); 

    // try to load any timestamps for this file 
    this.loadTS = function(ann) {  
    var xhr = new XMLHttpRequest(); 
    xhr.open("GET", window.location.protocol+"//"+ 
        window.location.hostname+"/sitename/timestamps/"+  
        window.location.pathname.substring(
        window.location.pathname.lastIndexOf('/')+1)+".json",  
       true); 
    xhr.onreadystatechange=function(){ 
     if(xhr.readyState==4 && xhr.status==200){ 
     console.log(xhr.responseText); 
     this.timestamps = JSON.parse(xhr.responseText); 
     for(var idx in this.timestamps){  
      this.audios[idx].addEventListener('loadedmetadata', this.stampX); 
      if(ann)  
      this.textIGTs[idx].setAttribute("class","igt existstamps");  
     } 
     } else console.log(xhr.status); 
    }.bind(this); 
    xhr.send();  
    } 

現在該功能只被調用一次,爆米花正常工作。儘管如此,仍然喜歡聽到關於上述錯誤的評論。