Only the final iteration triggers the addEventListener() function to execute

After spending hours trying to solve this, I've hit a roadblock. I've searched through similar questions on stackoverflow, but none of the solutions seem to work for me.

I'm currently looping through an array of videos and need to insert each video into the audio element to quickly retrieve its duration and position in the playlist.

This is what I originally had:

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

The code kept the index the same, causing the length of the last video in the array to be logged three times. After some investigation, I realized I needed to use closures due to adding event listeners inside the loop. So, I refactored it to the following version:

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

This update solved the issue with indexes, but I'm still only receiving the duration of the last video in the array.

console output

Any feedback would be greatly appreciated. This problem has been a real challenge for me.

Answer №1

When you repeatedly reset the source attribute of an audio element within a loop and add multiple event listeners, all events end up referring to the last item in the playlist.

To address this issue, consider loading audio sources asynchronously or creating a new audio element for each item in the playlist. Another option is to keep the duration unspecified until the user chooses which item to play.

It's important to note that creating a closure to store the index value is unnecessary. The let declaration within the for loop creates a distinct reference for each iteration, ensuring the correct index value is available for any function calls within the loop.

Answer №2

You have incorrectly created a closure in your code.

Please replace the following section of your code:

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

with

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

By doing this modification, you will be able to directly access the audio object inside the log function and retrieve its duration without relying on the event object.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The IMG onclick event for LinkModalDialog does not function properly on Mozilla browsers

I am currently utilizing the following function: function OpenLinkModal(obj){ var arr=showModalDialog("../../files/list_simple.asp","","dialogHeight: 600px; dialogWidth: 450px; edge: Raised; center: Yes; resizable: Yes; status: Yes; scroll: Yes; help ...

Tips for utilizing the onClick event:

Currently, I'm utilizing React.createElement in order to integrate React components into an existing HTML document. Despite successfully rendering the button within the returned div, I am encountering an issue where my button fails to respond when cli ...

How can I use Vue.js, Node.js, and Multer to offer visitors the choice to send their formData with or without a file upload in my application?

My frontend template using Vue.js includes: <template> <div class="d-flex justify-content-center"> <form @submit.prevent="submit" enctype="multipart/form-data" class="largeur80 my-5 shadow bordurePost bordureRond" ...

Failing to trigger the event when connected via addEventListener

I'm currently working on developing a function to calculate the number of characters remaining. My aim is to utilize addEventListener to monitor event types: var output = document.getElementById('output'); var tweetTxt = document.getEleme ...

What is the best way to change a multidimensional array into a multidimensional list using Kotlin?

I'm working on creating a grid for my Scrabble game and I wanted to use a 2D array to easily fill it with []. But now I need to convert it to a 2D list in order to access some useful methods. Does anyone know how to do this? I attempted to use a 2D l ...

Master the art of handling JSON data within an Angular controller

I've been spending a day attempting to manipulate a JSON in order to display a table, but I can't seem to achieve the desired outcome. My goal is to showcase statistics in a table for each town. If a town has multiple lines, I want to display a ...

Best practices for handling errors beyond network problems when using the fetch() function

I am facing a situation where the route of my fetch() call can result in two different responses, each requiring a different action. However, I have noticed that the catch() method only handles network errors as far as I know. Currently, my code looks lik ...

Steps to invoke the function that showcases the dynamic content of the initial tab using Angular Bootstrap upon the loading of the form or window

Utilizing an angular bootstrap tab, I am aware that the ng-click event on a tab will trigger any function upon clicking the tab. I am aiming to have some dynamic content load on the first tab when the form is loaded. The HTML code snippet is as follows: ...

What is the best way to showcase content without triggering a file download during a GET Request?

I recently created a webpage that showcases API responses in a neat tabular format. The technologies I used for this project were Angular JS, Servlets, and Java-Rest Assured framework. Each entry in the table includes a link to a log file, which is provid ...

What causes the discrepancy in margin values between desktop and mobile devices?

In my project, I have a collection of div elements that need to be spaced vertically from one another by the height of the window plus an additional 100px. However, I encountered a discrepancy when setting this margin using jQuery between mobile and deskto ...

By introducing the directive, the binding functionality becomes ineffective

Just starting out with Angular and working on a mobile app using angularjs and the ionic framework. I have an input field for project name that includes a directive to check if the name already exists, disabling data binding if it does. Any guidance would ...

Implement a select element with an onchange event that triggers an AJAX

As a newcomer to Javascript, I've been struggling to find a solution to my issue. The goal is to add an additional select option when the previous select option is changed. I attempted to implement the code below, but it's not functioning correct ...

Issue with AJAX request function not working correctly in JQuery

When attempting to display validation errors using an ajax request returned by a validator, I encountered an error in the console. Could this issue be related to my jQuery version (3.4.1)? Console output: TypeError: $.ajax(...).done(...).error is not a f ...

What is the process of developing an input formatting directive in AngularJS?

I am a beginner in AngularJS and I want to create a custom directive that removes leading zeroes. For example, if I enter 00014, I want the input field to display 14. I have some existing code but I'm not completely confident that it's correct: ...

Leveraging Angularfire2 to efficiently update multiple objects by utilizing data fan-out techniques

When it comes to updating objects in my database, I usually follow this process: const items = af.database.list('/items'); items.update('key-of-some-data1', { size: newSize1 }); items.update('key-of-some-data2', { size: newSi ...

What could be the reason why the angular time picker is not showing any

After using a time picker for booking and storing it in the database, I encountered an error while trying to pass the time to the input field during editing. Error: [ngModel:datefmt] Expected `01:00:00 pm` to be a date http://errors.angularjs.org/1.3.13/n ...

What could be causing the failure of this POST request using XMLHttpRequest to establish a connection with the server?

After performing a GET request on a URL that definitely exists, I attempted to open an XMLHttpRequest using the POST method. However, when I called the send() function on the XHR object, no connection was established with the server and no data was sent. E ...

Combining "AND" and "OR" operators within a Sequelize js scope

Dealing with some difficulties regarding the Sequelize JS. I'm attempting to incorporate the following query into a Sequelize scope. SELECT * FROM Projects WHERE isDeleted IS NOT true AND ( ( importSource IS NOT null ) AND ( createdAt BETWEEN ...

What is the method for combining a number with a variable designation?

Hey there, I'm having some trouble changing the variable name in a loop. I attempted to use a for loop with "stop" + i, but it didn't quite work out as I had hoped. Any assistance would be greatly appreciated! ...

How to handle a Node.js promise that times out if execution is not finished within a specified timeframe

return await new Promise(function (resolve, reject) { //some work goes here resolve(true) }); Using Delayed Timeout return await new Promise(function (resolve, reject) { //some work goes here setTimeout(function() { resolve(true); }, 5000); } ...