Image Source Not Updating on Audio Element State Change

31 views Asked by At

I'm implementing a basic audio player using Svelte (version 4.2.8), with SVG images for the interface icons, but the play/pause icons are not updating as expected.

Stripped-down version of the code:

<script>
    export let audioPath;
    let audioElement, playButton, seekBar;
    let currentlySeeking = false;

    const checkForAudioSource = () => !!audioElement.getAttribute('src');

    function togglePlayButton() {
        if(checkForAudioSource()) {
            if(audioElement.paused || audioElement.ended) audioElement.play();
            else audioElement.pause();
        }
    }

    // Seek bar event handling, from Develop HP: https://www.developphp.com/video/JavaScript/Audio-Seek-and-Volume-Range-Slider-Tutorial
    const startSeeking = () => currentlySeeking = true;
    function endSeeking() {
        currentlySeeking = false;
        if(checkForAudioSource()) audioElement.currentTime = Number(seekBar.value);
    }

    function updatePlayer() {
        if(checkForAudioSource() && !currentlySeeking) seekBar.value = audioElement.currentTime.toString();
        timeout = setTimeout(updatePlayer, 100);
    }

    onMount(() => updatePlayer());
</script>

<audio src={audioPath} preload="metadata" bind:this={audioElement}></audio>
<div id="audio-player">
    <button on:click={togglePlayButton} bind:this={playButton}>
        <img src={audioElement?.paused || audioElement?.ended ? "/svg/play.svg" : "/svg/pause.svg"} alt={audioElement?.paused || audioElement?.ended ? "Play" : "Pause"}>
    </button>
    <input type="range" value="0" step="0.1" on:mousedown={startSeeking} on:mouseup={endSeeking} bind:this={seekBar}>
</div>

Clicking the play/pause button does cause the audio file to play or pause as expected, and the seek bar updates as expected while playing and can be used to go to a specific moment in the sound file. However, the src and alt attributes of the play/pause button always remain "/svg/play.svg" and "Play" respectively, no matter when you click on the button (when the sound is at the beginning, the end, or in the middle).

I've tried doing some variations on the syntax of the event handling, but always got the same result. Is there something I'm missing here? Thanks so much in advance!

2

There are 2 answers

0
Hujaakbar On BEST ANSWER

the src and alt attributes of the play/pause button always remain "/svg/play.svg" and "Play" respectively, no matter when you click on the button

Because Svelte's reactivity is triggered by assignments, and updating object properties won't trigger reactivity. source

after updating audioElement.paused and audioElement.ended, reassign audioElement to itself.

audioElement = audioElement
0
Corrl On

Svelte has special bindings for Media elements including paused and ended (example with video)

<script>
  let paused
  let ended

    ...
</script>

<audio ... bind:paused bind:ended ></audio>
<div id="audio-player">
    <button ...>
        <img src={paused || ended ? "/svg/play.svg" : "/svg/pause.svg"} alt={paused || ended ? "Play" : "Pause"}>
    </button>
    ...
</div>