1

I am trying to make a component in Vue3 to manage audio. This component will display a button that when pressed, plays an audio (just a short word) and changes the color (to show it is playing the audio).

Currently I managed to play the audio, but I have some issues that I am not sure how to resolve, which arise because I don't really understand how the Audio object works in javascript:

  1. The class .playing is never applied to the element (isPlaying always returns false!)
  2. How can I check the audio gets loaded before playing it? In case the URL is not reachable the .play() call should not be executed. But I am not sure canplaythrough event will work for me, since I just want to load the audio once (and only if the user presses the button).
  3. What is the proper way of managing events for new Audio()? Try catch or I can do something with promises?
  4. How to unmount this component correctly? this.audio.pause(); this.audio.src = null?
<template>
<button v-on:click="play" :class="{playing: isPlaying}">Audio</button>
</template>
<script>
export default {
    name: 'AudioComponent',
    data() {
        return {
            audio: null,
        }
    },
    props: {
        url: {type: String, required: true}
    },
    computed: {
        isPlaying(){
            if(this.audio !== null){
                if(!this.audio.paused && this.audio.duration > 0){
                    return true;
                }
            }
            return false;
        }
    },
    methods: {
        play(){
            if(this.audio === null){
                this.audio = new Audio(this.url);
            }
            this.audio.play();
        }
    }
}
</script>
<style scoped>
.playing {
    color: green;
}
</style>

Note that this is a simplification of the code, but should be enough to see the issue. Since I couldn't find many examples using new Audio call I am wondering if I should use the DOM calls directly instead.

Note that I am using the options API but don't mind answers with composition API too.

2
  • 1
    isPlaying is never defined in your code. Commented Sep 5, 2022 at 10:37
  • Thanks for noticing, it was defined but lost when porting the code to stackoverflow. Commented Sep 5, 2022 at 18:41

1 Answer 1

1
+50
  1. this.audio properties are not reactive, so HTMLMediaElement events play and pause must be used.
  2. Yes, using the canplay event.
  3. You can listen for the error event as well, if that's your question.
  4. I don't think you need to do anything in particular.

Demo

{
    props: {
        url: {
            type: String,
            default: 'https://samplelib.com/lib/preview/mp3/sample-3s.mp3'
        }
    },
    data: () => ({
        audio: null,
        canPlay: false,
        isPlaying: false
    }),
    methods: {
        play: async function(){
            this.audio.play();
        }
    },
    mounted: function(){
        this.audio = new Audio(this.url);
        this.audio.addEventListener('canplay', () => this.canPlay = true, { once: true });
        this.audio.addEventListener('play', () => this.isPlaying = true);
        this.audio.addEventListener('pause', () => this.isPlaying = false);
        this.$watch(() => this.audio.duration, () => console.log(this.audio.paused));
    },
    template: `
        <button
            :class="{ playing: isPlaying }"
            :disabled="!canPlay"
            @click="play"
        >Audio</button>
    `
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks, this is more or less what I was looking for. One quick addition, if it is needed to check for errors (like network error, or file not found) another event listener can be added: this.audio.addEventListener('error', () => console.error('could not load audio file'));
You're welcome. Just like I said in reply to question no. 3, yes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.