Search code examples
sveltesvelte-component

Image Source Not Updating on Audio Element State Change


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!


Solution

  • 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