Search code examples
javascripthtmljqueryaudiohtml5-audio

Javascript Play All Audio Elements in a Div


I'm using bootstrap3_player to implement an audio player on my site. It works well but lacks the functionality to 'play all songs' - I have to click on each song in order to play it. I have the songs organized into albums. Clicking on an album opens a collapse that exposes the individual songs. Here's what an album div looks like.

<div class="collapse" id="album2">
  <audio controls="" data-info-att="Candy">
    <source src="albums/tulip16/Candy.mp3" type="audio/mpeg">
    <a href="albums/tulip16/Candy.mp3">Candy</a> 
    An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="My Red Corvette">
    <source src="albums/tulip16/My_Red_Corvette.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/My_Red_Corvette.mp3">My Red Corvette</a>
    An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="Winkie Don't Go">
    <source src="albums/tulip16/Winkie_Don't_Go.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Winkie_Don't_Go.mp3">Winkie Don't Go</a> 
    An html5-capable browser is required to play this audio.    
  </audio> 
  <audio controls="" data-info-att="Rain Stop Comin Down">
    <source src="albums/tulip16/Rain_Stop_Comin_Down.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Rain_Stop_Comin_Down.mp3">Rain Stop Comin Down</a>
    An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="Tulip">
    <source src="albums/tulip16/Tulip.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Tulip.mp3">Tulip</a> 
    An html5-capable browser is required to play this audio.    
  </audio> 
  <audio controls="" data-info-att="Independence Game">
    <source src="albums/tulip16/Independence_Game.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Independence_Game.mp3">
    Independence Game</a> An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="Horoscope"><source src="albums/tulip16/Horoscope.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Horoscope.mp3">Horoscope</a>
     An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="Sometimes I Cry"><source src="albums/tulip16/Sometimes_I_Cry.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Sometimes_I_Cry.mp3">Sometimes I Cry</a> 
    An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="I Don't Know (Why She Did That to You)">
    <source src="albums/tulip16/I_Don't_Know_(Why_She_Did_That_To_You).mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/I_Don't_Know_(Why_She_Did_That_To_You).mp3">I Don't Know (Why She Did That to You)</a> 
    An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="Show Me Tonight">
    <source src="albums/tulip16/Show_Me_Tonight.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Show_Me_Tonight.mp3">Show Me Tonight</a> 
    An html5-capable browser is required to play this audio.
  </audio> 
  <audio controls="" data-info-att="Slums of San Marino">
    <source src="albums/tulip16/Slums_of_San_Marino.mp3" type="audio/mpeg"> 
    <a href="albums/tulip16/Slums_of_San_Marino.mp3">Slums of San Marino</a> 
    An html5-capable browser is required to play this audio.
  </audio>
</div>

Here's a link to the bootstrap3_player js. https://github.com/iainhouston/bootstrap3_player/blob/master/js/bootstrap3_player.js Can someone point me to how to add the 'play all songs' functionality to bootstrap3_palyer.js OR How I might create a separate HTML5 web audio function that would allow me to play all the songs in the div. Thanks!


Solution

  • You can select all of the <audio> elements in the <div> of interest, then loop over them calling .play() on each one. This would incorrectly play all audio files at the same time, but you can use the onended event to wait for each playback to complete before moving on to the next one.

    This is sort of an aside, but I'd suggest keeping all of your data in an array, then looping over it to generate the elements. This keeps the code much more DRY.

    Here's a minimal example/proof-of-concept you can adapt to your relatively complex-looking codebase:

    const srcs = [
      "https://upload.wikimedia.org/wikipedia/commons/2/2e/En-us-Wikipedia.ogg",
      "https://upload.wikimedia.org/wikipedia/commons/a/a1/De-Johann_Sebastian_Bach.ogg",
      "https://upload.wikimedia.org/wikipedia/commons/9/90/De-Wolfgang_Amadeus_Mozart.ogg",
      "https://upload.wikimedia.org/wikipedia/commons/8/8e/Drums.ogg",
    ];
    
    for (const src of srcs) {
      const audio = document.createElement("audio");
      document.querySelector("#sounds").append(audio);
      audio.controls = "controls";
      audio.src = src;
    }
    
    document
      .querySelector("#play-all")
      .addEventListener("click", async e => {
        const sounds = document.querySelectorAll("#sounds audio");
        
        for (const sound of sounds) {
          const ended = new Promise(resolve =>
            sound.addEventListener("ended", resolve, {once: true})
          );
          sound.play();
          await ended;
        }
      })
    ;
    <button id="play-all">Play all</button>
    <div id="sounds"></div>

    With a bit of additional bookkeeping, it should be easy to modify this to let the user toggle autoplay, for example. You could add a permanent onended listener to each audio element that checks a flag to determine whether to trigger playback on the next sibling audio element, if it exists, then buttons to flip flags and operate on whichever audio element is currently playing (for example, to pause it).