2016-10-03 132 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();  
    } 

现在该功能只被调用一次,爆米花正常工作。尽管如此,仍然喜欢听到关于上述错误的评论。