仅在上一个循环周期调用addEventListener()

时间:2018-11-27 04:09:44

标签: javascript arrays loops closures addeventlistener

我一直在努力解决这个问题,运气不好。 浏览了所有关于stackoverflow的类似问题,但没有一个解决方案对我有用。

我正在播放一系列视频。我需要将每个视频插入音频元素,以快速获取其持续时间以及确切的时间出现在播放列表中。

我本来是这样的:

// Set time in playlist for each video
for (let index=0; index<this.playlist.length; index++) {
  var audio = document.getElementById('audio');
  console.log('INDEX: ' + index);
  self.playlist[index].timeInPlaylist = [];
  self.playlist[index].duration = 0;
  audio.setAttribute('src', self.playlist[index].bucketRef);
  audio.addEventListener('canplaythrough', function(e){
     videoDuration = Math.round(e.currentTarget.duration);
     console.log("Duration of video number " + index + ": " + 
     videoDuration + " seconds");
     self.playlist[index].duration = videoDuration;
     self.playlist[index].timeInPlaylist[0] = playlistLength;
     playlistLength = playlistLength + videoDuration;
     self.playlist[index].timeInPlaylist[1] = playlistLength;
  });
};

运行此代码可使索引保持不变,并且输出是数组中记录的3次视频的最后一个视频的长度。然后,在深入研究之后,我意识到我需要某种类型的闭包,因为在循环内添加了事件侦听器。因此,我做了一些重构,来到了当前版本:

function log(e, index) {
  videoDuration = Math.round(e.currentTarget.duration);
  console.log("Duration of video number " + index + ": " + videoDuration + " seconds");
  self.playlist[index].duration = videoDuration;
  self.playlist[index].timeInPlaylist[0] = playlistLength;
  playlistLength = playlistLength + videoDuration;
  self.playlist[index].timeInPlaylist[1] = playlistLength;
}

// Set time in playlist for each video
for (let index=0; index<this.playlist.length; index++) {
  var audio = document.getElementById('audio');
  console.log('INDEX: ' + index);
  self.playlist[index].timeInPlaylist = [];
  self.playlist[index].duration = 0;
  audio.setAttribute('src', self.playlist[index].bucketRef);
  debugger;
  (function() {
      var i = index;
      audio.addEventListener("canplaythrough", function(e) { log(e, i); });
  })();
};

这解决了我的索引问题;但是,我仍然只获得数组中最后一个视频的时长。

console output

我非常感谢您的反馈。这个问题使我丧命。

2 个答案:

答案 0 :(得分:0)

您创建了一个关闭,但是方式不正确。

将此部分替换为您的代码

(function() { var i = index; audio.addEventListener("canplaythrough", function(e) { log(e, i); }); })();

audio.addEventListener("canplaythrough", (function(audioObj, index) {
    return function(){
      log(audioObj, index); 
    }
  })(this, index)
 );

这样做应该可以直接访问 log 函数中的音频对象以获取其持续时间,而不是依赖于事件对象。

答案 1 :(得分:0)

您将在循环中同步重置同一音频元素的source属性,并将额外的事件侦听器添加到一个音频元素。

在循环终止并且音频元素为其设置的最后一个src属性加载音频之后,将调用所有事件监听器,并提供相同的详细信息上一个播放列表项。

您将需要重新设计代码以异步运行,并依次为一个音频元素加载不同的音频源,或者为每个播放列表项创建一个新的AUDIO元素。或者,将持续时间留为空白,直到用户选择要播放的项目(只是一个想法)。

请注意,您不需要创建闭包来保存index的值-这是调试时的错误:let的for循环声明中的index创建了一个单独的对每个循环迭代的变量的词法引用。在循环内的函数表达式中看到的index的值是对表达式求值时的迭代的值。