Search code examples
javascripthtmlaudiocustom-data-attribute

JavaScript: How can I set src in the JavaScript code based on data attributes in the HTML code?


I want to use 6 of these Before/After Audio Players on my website: https://github.com/mattbartley/AB-Audio-Player

However, the sources of the audio files are set in the JavaScript code. So when I implement multiple instances of this player in my HTML, they all play the same two audio files. I want to set data attributes in the HTML code in each player div for the src of the files and let the JavaScript use that instead. How do I go about it?

<div class="player__wrapper" data-audio-before="Song-1-before.mp3" data-audio-after="Song-1-after.mp3">

...

<div class="player__wrapper" data-audio-before="Song-2-before.mp3" data-audio-after="Song-2-after.mp3">

...

And so on. This would be the updated HTML code.

I think it's a pretty basic solution, I'm just not good in JavaScript.

Here is the current JavaScript code:

//Set up audio elements
var soundA = document.createElement("audio");
//Set audio A src here
soundA.src = "./assets/a.mp3";
soundA.preload = "auto";
soundA.setAttribute("hidden", "true");
soundA.setAttribute("onplaying", "stepA()");
document.body.append(soundA);

var soundB = document.createElement("audio");
//Set audio B src here
soundB.src = "./assets/b.mp3";
soundB.preload = "auto";
soundB.setAttribute("hidden", "true");
soundB.setAttribute("onplaying", "stepB()");
document.body.append(soundB);

I tried something like this: Hover over div, use its data-attribute to change the src of an img And other approaches as well. Couldn't get it to work.


Solution

  • I forked the Matt Bartley 's AB-Audio-Player - not great but it works with multiple instances:

    As commented, the player initialization is done within a forEach loop.
    All elements are selected by class names to avoid non unique IDs and relative element selection.

    So you need to update your HTML template accordingly.

    let players = document.querySelectorAll(".player__wrapper");
    initPlayers(players);
    
    function initPlayers(players) {
      players.forEach((player) => {
        //Get button elements
        const playBtns = player.querySelectorAll(".ab__button");
        const aButton = player.querySelector(".a__button");
        const bButton = player.querySelector(".b__button");
        const playButton = player.querySelector(".play__button");
        const stopButton = player.querySelector(".stop__button");
        const progressBar = player.querySelector(".progress__bar");
        const progressFill = player.querySelector(".progress__fill");
    
        // set icons
        const playIcon = '<i class="fa-solid fa-play"></i>';
        const pauseIcon = '<i class="fa-solid fa-pause"></i>';
        const stopIcon = '<i class="fa-solid fa-stop"></i>';
    
        //Default loading state for each sound
        var soundAReady = false;
        var soundBReady = false;
    
        //Set up audio elements
        var soundA = document.createElement("audio");
        soundA.src = player.getAttribute("data-sound-a");
        soundA.preload = "auto";
        soundA.setAttribute("hidden", "true");
        player.append(soundA);
    
        var soundB = document.createElement("audio");
        soundB.src = player.getAttribute("data-sound-b");
        soundB.preload = "auto";
        soundB.setAttribute("hidden", "true");
        player.append(soundB);
    
        //playSoundA
        aButton.addEventListener("click", (e) => {
          pauseAll();
          playButton.innerHTML = pauseIcon;
          aButton.disabled = true;
          bButton.disabled = false;
          stopButton.disabled = false;
          soundA.currentTime = soundB.currentTime;
          soundA.play();
        });
    
        //playSoundB
        bButton.addEventListener("click", (e) => {
          pauseAll();
          playButton.innerHTML = pauseIcon;
          bButton.disabled = true;
          aButton.disabled = false;
          stopButton.disabled = false;
          soundB.currentTime = soundA.currentTime;
          soundB.play();
        });
    
        //playSoundA
        soundA.addEventListener("playing", (e) => {
          console.log("playing");
          progressFill.style.width =
            ((soundA.currentTime / soundA.duration) * 100 || 0) + "%";
          requestAnimationFrame(stepA);
        });
    
        //playSoundB
        soundB.addEventListener("playing", (e) => {
          console.log("playing B");
          progressFill.style.width =
            ((soundB.currentTime / soundB.duration) * 100 || 0) + "%";
          requestAnimationFrame(stepB);
        });
    
        // playPause
        playButton.addEventListener("click", (e) => {
          if (soundA.paused & soundB.paused) {
            let soundATime = soundA.currentTime;
            let soundBTime = soundB.currentTime;
            if (soundATime >= soundBTime) {
              soundA.play();
              bButton.disabled = false;
              aButton.disabled = true;
              playButton.innerHTML = pauseIcon;
            } else {
              soundB.play();
              bButton.disabled = true;
              aButton.disabled = false;
              playButton.innerHTML = pauseIcon;
            }
            stopButton.disabled = false;
          } else {
            playButton.innerHTML = playIcon;
            soundA.pause();
            soundB.pause();
          }
        });
    
        // stop
        stopButton.addEventListener("click", (e) => {
          playButton.innerHTML = playIcon;
          aButton.disabled = false;
          bButton.disabled = true;
          playButton.disabled = false;
          stopButton.disabled = true;
          soundA.pause();
          soundA.currentTime = 0;
          soundB.pause();
          soundB.currentTime = 0;
        });
    
        //Check for mobile to enable audio playback without waiting for download status.
        if (
          /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
            navigator.userAgent
          )
        ) {
          playButton.disabled = false;
        }
    
        //Default loading state for each sound
        var soundAReady = false;
        var soundBReady = false;
    
        //When audio can play through (loaded), run the function to enable buttons
        //The canplaythrough event will fire every time the audio switches, so the !soundA/BReady
        //prevents additional checks
        soundA.oncanplaythrough = function() {
          if (!soundAReady) {
            soundAReady = true;
            audioIsReady();
          }
        };
        soundB.oncanplaythrough = function() {
          if (!soundBReady) {
            soundBReady = true;
            audioIsReady();
          }
        };
    
        // Check if both A & B are ready and enable the correct buttons
        function audioIsReady() {
          if (soundAReady && soundBReady) {
            console.log("...audio loaded!");
            aButton.disabled = false;
            playButton.disabled = false;
          } else {
            console.log("Audio loading...");
          }
        }
    
        const progress = player.querySelector(".progress");
        // Listen for click on entire progress bar div (to allow skipping ahead)
        progress.addEventListener("click", function(event) {
          // Get X coordinate of click in div
          var rect = this.getBoundingClientRect();
          // Convert click position to percentage value
          var percentage = (event.clientX - rect.left) / this.offsetWidth;
          // Seek to the percentage converted to seconds
          soundA.currentTime = percentage * soundA.duration;
          soundB.currentTime = percentage * soundB.duration;
        });
    
        //Frame animations for progress bar fill - converts to CSS percentage
        function stepA() {
          progressFill.style.width =
            ((soundA.currentTime / soundA.duration) * 100 || 0) + "%";
          requestAnimationFrame(stepA);
        }
    
        function stepB() {
          progressFill.style.width =
            ((soundB.currentTime / soundB.duration) * 100 || 0) + "%";
          requestAnimationFrame(stepB);
        }
    
        //Play/Stop correct audio and toggle A/B, Play/Pause, and Stop buttons
        function playPause() {
          if (soundA.paused & soundB.paused) {
            let soundATime = soundA.currentTime;
            let soundBTime = soundB.currentTime;
            if (soundATime >= soundBTime) {
              soundA.play();
              bButton.disabled = false;
              aButton.disabled = true;
              playButton.innerHTML = pauseIcon;
            } else {
              soundB.play();
              bButton.disabled = true;
              aButton.disabled = false;
              playButton.innerHTML = pauseIcon;
            }
            stopButton.disabled = false;
          } else {
            playButton.innerHTML = playIcon;
            soundA.pause();
            soundB.pause();
          }
        }
    
        // optional: set auto ids
        let allAudio = document.querySelectorAll("audio");
        allAudio.forEach((audio, i) => {
          audio.id = "audio_" + i;
        });
    
        // rewind all at end
        allAudio.forEach((audio) => {
          //audio.pause();
          audio.addEventListener("ended", (e) => {
            audio.currentTime = 0;
            progressFill.style.width = "0%";
          });
        });
    
    
        function pauseAll() {
          let allAudio = document.querySelectorAll("audio");
          allAudio.forEach((audio) => {
            audio.pause();
          });
        }
    
    
    
      });
    }
    <link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap" rel="stylesheet" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" integrity="sha512-KfkfwYDsLkIlwQp6LFnl8zNdLGxu9YAA1QvwINks4PhcElQSvqcyVLLD9aMhXd13uQjoXtEKNosOWaZqXgel0g==" crossorigin="anonymous" referrerpolicy="no-referrer"
    />
    
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player@main/css/style.css" />
    
    <div class="player__wrapper" data-sound-a="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player@main/assets/a.mp3" data-sound-b="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player@main/assets/b.mp3">
      <div class="progress__container progress">
        <div class="progress__bar progress__fill"></div>
      </div>
      <div class="ab__controls">
        <button class="ab__button a__button" disabled="true">
              A
            </button>
        <button class="ab__button b__button" disabled="true">
              B
            </button>
      </div>
      <div class="play__stop__controls">
        <button class="play__pause__button play__button" disabled="true">
              <i class="fa-solid fa-play"></i>
            </button>
        <button class="play__pause__button stop__button" disabled="true">
              <i class="fa-solid fa-stop"></i>
            </button>
      </div>
    </div>
    
    
    <div class="player__wrapper" data-sound-a="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player@main/assets/a.mp3" data-sound-b="https://cdn.jsdelivr.net/gh/mattbartley/AB-Audio-Player@main/assets/b.mp3">
      <div class="progress__container progress">
        <div class="progress__bar progress__fill"></div>
      </div>
      <div class="ab__controls">
        <button class="ab__button a__button" disabled="true">
              A
            </button>
        <button class="ab__button b__button" disabled="true">
              B
            </button>
      </div>
      <div class="play__stop__controls">
        <button class="play__pause__button play__button" disabled="true">
              <i class="fa-solid fa-play"></i>
            </button>
        <button class="play__pause__button stop__button" disabled="true">
              <i class="fa-solid fa-stop"></i>
            </button>
      </div>
    </div>