skip to Main Content

When I pause an Audio and set it’s currentTime, it does something weird. when I press a button that controls the .paused state of the Audio, it seems to click again automatically, preventing the audio from playing. Why?

Additionally, if I set the currentTime again, it decides to stop clicking the play button twice. this happens on and off every time I set the currentTime. it can be demonstrated with this code. (slowed audio is only because the example audio is too short).

<!doctype html><html><body>
  <button id="play" disabled>play</button>
  <button id="idk">load audio</button>
  <script>
    var clicks = 0; console.clear();
    idk = document.getElementById("idk");
    idk.addEventListener("click", async ()=>{
      aud = new Audio("https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3")
      aud.addEventListener('canplaythrough', init);
    }, {once: true});
    function init(){
      /**/aud.playbackRate = 0.25;//only to lengthen the audio to give you time.
      playButton = document.getElementById("play");
      playButton.disabled = false;
      idk.innerText = "pause and set time to 1";
      playButton.addEventListener("click", e=>{
        if(aud.paused){
          aud.play();
          playButton.innerText = "pause";
        }else{
          aud.pause();
          playButton.innerText = "play";
        }
        //Nothing different between duplicate click events, logging it takes up space
        console.log(clicks++, aud.paused, /*e*/);//clicks++ to tell apart logs.
      });
      idk.addEventListener("click", e=>{
        console.log("Rightmost element clicked");
        if(!aud.paused){ aud.pause(); playButton.innerText = "play"; }
        aud.currentTime = 1;
      });
    }
  </script>
</body></html>

See live example (removed event logging due to pollution of console)

simply click load audio, then click play once permitted, and finally click on pause and set time to 1.

after such, the click event will be fired twice per actual click (as seen in the log), preventing the audio from playing. the only way to fix it is to click pause and set time to 1 again, and attempt clicking play. You will be successful, but if you click the right button again, you will face this issue again.

I also noticed that the double-clicking turns into quadruple-clicking when modified slightly, still causing the issue, as 4 clicks is an even number.

<!doctype html><html><body>
  <button id="play">play</button>
  Control+Click to pause the audio and change it's currentTime value
  <script>
    var clicks = 0; console.clear();    
    playButton = document.getElementById("play");
    playButton.addEventListener("click", async ()=>{
      /**/window.aud = new Audio("https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3");
      aud.addEventListener('canplaythrough', ()=>{
        playButton.style.color = "#080";
        /**/aud.playbackRate = 0.25;//only to lengthen the audio to give you time.
        playButton.addEventListener("click", e=>{
          if(e.ctrlKey){
            if(!aud.paused){ aud.pause(); playButton.innerText = "play"; }
            aud.currentTime = 1;
            return;
          }
          if(aud.paused){
            aud.play();
            playButton.innerText = "pause";
          }else{
            aud.pause();
            playButton.innerText = "play";
          }
          //Nothing different between duplicate click events, logging it takes up space
          console.log(clicks++, aud.paused, /*e*/);//clicks++ to tell apart logs.
        });
      });
    }, {once: true});
  </script>
</body></html>

See live example (removed event logging due to pollution of console)

to use the second example, use CTRL+Click as the pause and set time to 1 button.

in both examples, running aud.play(); in the console seems to work, but still doesn’t allow the play/pause button to act and double clicks them. Setting aud.currentTime in the console (after it has been set an even number of times) has no effect, and neither does having the code set aud.currentTime twice in succession. The user has to click it and then click it again.

This occurs on Chrome Beta 127.0.6533.94 on ChromeOS, as well as Chrome 127.0.6533.85 and Firefox 129.0 on Android 14. I Can’t get it to work at all on non-EU iOS.

HOWEVER…

Safari 17.3 on MacOS Sonoma (via Browserling, if any difference made) appears to work as expected.

Sorry in advance, if that’s insufficient testing, or I made a really obvious mistake

2

Answers


  1. To solve your problem, you have a few points to consider:

    1. Event Listener Duplication : Each time you click the play button, a new event listener is added. This can cause multiple event listeners to be triggered, leading to the double-click behavior. To fix this, ensure that the event listener is added only once.

    2. Browser Differences: Different browsers, like in Google Chrome, handle the currentTime property differently, as told in this StackOverflow question, and in the MDN Docs: BaseAudioContext: currentTime property.

    Your problem, in this case, is that you triggered multiple times the click event on the play button, due to event duplication. Here’s the code:

    <!doctype html>
    <html>
    <body>
      <button id="play" disabled>play</button>
      <button id="idk">load audio</button>
      <script>
        var clicks = 0;
        console.clear();
        idk = document.getElementById("idk");
        idk.addEventListener("click", async () => {
          aud = new Audio("https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3");
          aud.addEventListener('canplaythrough', init, { once: true });
        }, { once: true });
    
        function init() {
          aud.playbackRate = 0.25; // only to lengthen the audio to give you time.
          playButton = document.getElementById("play");
          playButton.disabled = false;
          idk.innerText = "pause and set time to 1";
    
          playButton.addEventListener("click", togglePlayPause, { once: true });
          idk.addEventListener("click", setTimeToOne, { once: true });
        }
    
        function togglePlayPause() {
          if (aud.paused) {
            aud.play();
            playButton.innerText = "pause";
          } else {
            aud.pause();
            playButton.innerText = "play";
          }
          console.log(clicks++, aud.paused);
          playButton.addEventListener("click", togglePlayPause, { once: true });
        }
    
        function setTimeToOne() {
          console.log("Rightmost element clicked");
          if (!aud.paused) {
            aud.pause();
            playButton.innerText = "play";
          }
          aud.currentTime = 1;
          idk.addEventListener("click", setTimeToOne, { once: true });
        }
      </script>
    </body>
    </html>
    

    This ensures that the togglePlayPause function is called only once per click, preventing the multiple event listener issue.

    Login or Signup to reply.
  2. Here’s a much cleaner approach:

    • Avoid creating event, functions, within other event functions, otherwise you’ll end up incrementally stacking new callbacks on every event ("click"). One way is to use {once: true} as the Event options (third argument) – but that adds complexity to the code and it’s hard to reason about and debug.
    • instantiate your variables properly (using const, let) to prevent polluting the global scope
    • set const aud = new Audio(); immediately in your variables. You’ll add .source later
    • create three functions that do exactly what they say, namely: load() (with the specific pause + time thing), toggle() to toggle play/pause, and updateUI() which only job is to take care of the UI depending on the aud.paused and aud.readyState states
    • make use of the Audio "canplaythrough", "play" and "pause" Events in order to call the changeUI
    • Your load and play-pause Elements should (on click) call respectively the load() and toggle() functions
    const source = "https://upload.wikimedia.org/wikipedia/en/3/39/Metallica_-_Enter_Sandman.ogg";
    const elLoad = document.querySelector("#load");
    const elPlay = document.querySelector("#play");
    const aud = new Audio(); // Set Audio constructor right away
    
    // FUNCTIONS
    const load = () => {
      aud.pause();
      if (aud.src !== source) aud.src = source;
      else aud.currentTime = 5;
    };
    const toggle = () => {
      aud[aud.paused ? "play" : "pause"]();
    };
    const updateUI = () => {
      elPlay.disabled = !aud.readyState;
      elPlay.textContent = aud.paused ? "Play" : "Pause";
      elLoad.textContent = aud.readyState ? "Pause and set time to 5s" : "Load audio";
    };
    
    // EVENTS
    aud.addEventListener("canplaythrough", updateUI);
    aud.addEventListener("pause", updateUI);
    aud.addEventListener("play", updateUI);
    elLoad.addEventListener("click", load);
    elPlay.addEventListener("click", toggle);
    
    // INIT
    updateUI();
    <button id="play" disabled></button>
    <button id="load"></button>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search