-2

I have some dynamic cards:

enter image description here

and I want to add eventListener to all play buttons (their code is):

<a href="link">
    <img src="play.svg">
    <audio src="myMusic.mp3"></audio>
</a>

When I click to "play", it should look like below:

enter image description here

(it should change image and should also play audio)

My JS code so far done is:

<script>
var imgsPlay = document.getElementsByClassName('play-img-loop');
var audioPlayers = document.getElementsByClassName('audio-player');

window.onload = function() {
        for(var i = 0; i < imgsPlay.length; i++) {
            imgsPlay[i].addEventListener('click', function(event) {

                if(this.getAttribute('src') == "img/icons/play.svg") {
                    this.setAttribute('src', "img/icons/play_red.svg");
                    audioPlayers[i].play();

                } else if(this.getAttribute('src') == "img/icons/play_red.svg") {
                    this.setAttribute('src', "img/icons/play.svg");
                    audioPlayers[i].pause();
                }
                
            });
        }    
    }

</script>

(I can do it manually, but) can not dynamically. How can I do this ?

2 Answers2

1

You need to delegate! If not, then your question is just a dupe of JavaScript closure inside loops – simple practical example

Here I assume you have a container called cardContainer

I am a little confused as to _red means playing or paused, so change the code below to match. Your example HTML does not match the code you show, there are no classes on the player and image, I therefor assume you do have those in the actual code

window.addEventListener('load', function() {
  document.getElementById('cardContainer').addEventListener('click', function(e) {
    const tgt = e.target;
    if (tgt.classList.contains('play-img-loop')) {
      const src = tgt.getAttribute('src');
      const running = src.includes('_red');
      const audioPlayer = tgt.closest('a').querySelector('audio');
      tgt.setAttribute('src', `img/icons/play$(running?'':'_red').svg`);
      if (running) audioPlayer.pause();
      else audioPlayer.play();
    }
  });
});
mplungjan
  • 169,008
  • 28
  • 173
  • 236
0

Your problem is the incorrect handling of closures. The event listener call back will never get the correct value of i by the way you have written it. The value of i will always be the last one when the events are fired.

The easiest workaround is to define the variable i only for the scope of a single iteration - which is what the let do.

    for(let i = 0; i < imgsPlay.length; i++) {
        imgsPlay[i].addEventListener('click', function(event) {

            if(this.getAttribute('src') == "img/icons/play.svg") {
             ...
    }    

Useful reference: setTimeout in for-loop does not print consecutive values

Charlie
  • 22,886
  • 11
  • 59
  • 90