Search code examples
javascripthtmlbuttondom-eventshtml5-audio

How to associate a particular JavaScript function to every button


I have successfully made a html page where I am able to create a soundboard of different audio files associated to individual buttons. Each button plays an audio file, and when you click on a different button, the previous audio file stops playing and the next audio file starts playing. For that, I created an onplay JavaScript function that applies to all buttons globally on the page.

function pauseOthers(ele) 
{
  $("audio").not(ele).each(function (index, audio)
  {
    audio.pause();
    audio.currentTime = 0;
  });
}

Now, if I want to stop playing the audio that is currently selected, I have made a function Stop() associated to an onclick parameter to stop audio playback associated to the button:

function Stop()
{
    var Audio = document.getElementById("sound1");
    if(Audio.paused) {
        Audio.play();
    }
    else {
       Audio.pause(); Audio.currentTime = 0
    }
}

The corresponding source code is as follows:

<button type="button" class="btn btn-lg btn-block" onclick="Stop()">Dream for Dreaming</button>
<audio hidden preload="auto" id="sound1" controls onplay="pauseOthers(this);">
    <source src="AUDIO FILES\Dream for Dreaming.mp3" type="audio/mpeg">
</audio>

However, this approach of applying different Stop() variables (e.g., Stop2(), Stop3(), etc.) each associated to different audio file ids is not efficient, and I clearly see that I need to use an alternative approach instead of using getElementById. I do not know how to program this JavaScript function to be global for all buttons selected. Instead of creating a separate function for each audio file, how can each audio file be called by a single JavaScript function to stop playing when I click on different buttons?


Solution

  • Group buttons and audio elements together inside HTML and then use .closest and .find to get the associated audio element.

    It's also a good practice to separate HTML and JavaScript, so the usage of on* attributes is discouraged and you should attach event handlers through .on jQuery methods.
    Ideally you would use jQuery's event delagation, but media event's don't work with that.

    function playStop()
    {
        const audio = $(this).closest(".soundboard-item").find("audio").get(0);
        if(audio.paused) {
            audio.play();
        }
        else {
           audio.pause();
           audio.currentTime = 0;
        }
    }
    
    function pauseOthers() {
        $("#soundboard audio").not(this).each(function(index, audio) {
            audio.pause();
            audio.currentTime = 0
        });
    }
    
    $("#soundboard .soundboard-item").each(function(i, item) {
        $(item).find("button").on("click", playStop);
        $(item).find("audio").on("play", pauseOthers);
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div id="soundboard">
        <div class="soundboard-item">
            <button type="button" class="btn btn-lg btn-block">Star Wars</button>
            <audio hidden preload="auto" controls>
                <source src="https://www2.cs.uic.edu/~i101/SoundFiles/StarWars3.wav">
            </audio>
        </div>
        <div class="soundboard-item">
            <button type="button" class="btn btn-lg btn-block">Cantina Band</button>
            <audio hidden preload="auto" controls>
                <source src="https://www2.cs.uic.edu/~i101/SoundFiles/CantinaBand3.wav">
            </audio>
        </div>
     </div>