Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

DanielRønfeldt's avatar

Show loading progress for <video> HTML element

What I'm trying to achieve, as the title says, is to display the loading progress of a <video> HTML element during its loading. I don't need any "video player" plugin or component, no need for fancy yet overkill stuff like that.

Right now, as expected, the videos are loading instantaneously when served locally (no surprise here), but there's nothing visible until any of them is completely loaded when in production mode. Again, no surprise here. I just need to "inform" the visitor that something is being loaded, especially considering that I'll be using videos that are rather large in filesize. I was thinking about video loading events (if there even is such a thing), but I couldn't find any reliable info on that.

Here's a simplified version of my code. The radio buttons group serves as a "selector" which updates the video source as chosen by the visitor.

// VideoPreview.vue
<template>
  <div class="video-wrapper>
    <video :src="currentVideo" autoplay muted loop>Video unavailable</video>
  </div>
  <div class="radio-wrapper">
    <div v-for="{type, idName} in videoTypes" :key="type.id">
      <label :for="idName">{{ type.name }}</label>
      <input type="radio" v-model="selectedVideo" :value="idName" @change="handleVideoChange">
    </div>
  <div>
</template>

<script>
export default {
  name: "VideoPreview",
  
  data() {
    return {
      currentVideo: this.initialVideo,
      selectedVideo: '',
    }
  },
  
  props: {
    "initialVideo": { type: String },
    "videoTypes": { type: Object }
  },
  
  methods: {
    handleVideoChange() {
      this.currentVideo = this.selectedVideo;
    }
  }
}
</script>

Any thoughts? Thanks a lot!

0 likes
11 replies
Tray2's avatar

The easiest way is to create a spinner

<section id="loading">
    <div id="loading-content"></div>
</section>
.loading {
	z-index: 20;
	position: absolute;
	top: 0;
	left:-5px;
	width: 100%;
	height: 100%;
    background-color: rgba(0,0,0,0.4);
}
.loading-content {
	position: absolute;
	border: 16px solid #f3f3f3; /* Light grey */
	border-top: 16px solid #3498db; /* Blue */
	border-radius: 50%;
	width: 50px;
	height: 50px;
	top: 40%;
	left:35%;
	animation: spin 2s linear infinite;
	}
	
	@keyframes spin {
		0% { transform: rotate(0deg); }
		100% { transform: rotate(360deg); }
	}
function showLoading() {
  document.querySelector('#loading').classList.add('loading');
  document.querySelector('#loading-content').classList.add('loading-content');
}

function hideLoading() {
  document.querySelector('#loading').classList.remove('loading');
  document.querySelector('#loading-content').classList.remove('loading-content');
}
DanielRønfeldt's avatar

@tray2 thanks for the nice idea, I'm definitely planning on using it. However, I still have no clue as to how I could actually connect the spinner to the actual video loading process, as in, which events should I hook into. Any thoughts on that?

DanielRønfeldt's avatar

As a matter of fact, I have not. Thank you so much for pointing me into the right direction.

DanielRønfeldt's avatar

Still no go. The video element seems to be undefined even after referencing it and trying logging it in the console within the mounted() hook. I've got no clue as to how I could attach the event listener to it.

// VideoPreview.vue
// added ref attribute to the video element

<template>
  <div class="video-wrapper>
    <video ref="videoObj" :src="currentVideo" autoplay muted loop>Video unavailable</video>
  </div>
  <div class="radio-wrapper">
    <div v-for="{type, idName} in videoTypes" :key="type.id">
      <label :for="idName">{{ type.name }}</label>
      <input type="radio" v-model="selectedVideo" :value="idName" @change="handleVideoChange">
    </div>
  <div>
</template>

<script>
export default {
  name: "VideoPreview",
  
  data() {
    return {
      currentVideo: this.initialVideo,
      selectedVideo: '',
    }
  },
  
  props: {
    "initialVideo": { type: String },
    "videoTypes": { type: Object }
  },
  
  mounted() {
    console.log( this.$refs.videoObj ); // undefined
  },
  
  methods: {
    handleVideoChange() {
      this.currentVideo = this.selectedVideo;
    }
  }
}
</script>
DanielRønfeldt's avatar

@sinnbeck totally agreed, that was a silly mistake of mine, due to copy-pasting my code and removing the irrelevant parts, thanks! However, I'm still getting undefined when trying to output the video element to the console. Do you have any idea as to why this is happening?

DanielRønfeldt's avatar

I decided to take a different approach, as in, use Axios to handle the asynchronous video loading requests. That way, I'm certain I'll be able to handle video loading events the way I need it. I'll report back with my solution :)

1 like
DanielRønfeldt's avatar
Level 10

I'm back with my solution. In a nutshell, I'm leveraging the Axios library to take advantage of the video loading events and act accordingly based on their occurrance. More specifically, showing a "loading spinner" element until the video has finished loading (the "spinner" is not included in my code for the sake of brevity). Hope it helps someone :)

// VideoPreview.vue
// added Axios-driven async video loading

<template>
  <div class="video-wrapper">
    <video :src="currentVideo" autoplay muted loop>Video unavailable</video>
  </div>
  <div class="radio-wrapper">
    <div v-for="{type, idName} in videoTypes" :key="type.id">
      <label :for="idName">{{ type.name }}</label>
      <input type="radio" v-model="selectedVideo" :value="idName" @change="handleVideoChange">
    </div>
  <div>
</template>

<script>
export default {
  name: "VideoPreview",
  
  data() {
    return {
      currentVideo: this.initialVideo,
      selectedVideo: '',
    }
  },
  
  props: {
    "initialVideo": { type: String },
    "videoTypes": { type: Object }
  },
  
  mounted() {
    //...
  },
  
  methods: {
    handleVideoChange() {
      this.currentVideo = this.selectedVideo;
      // show video loading spinner...
      
      // Use Axios to handle video loading <------ this is where the important part begins :)
      axios.get(this.currVideoPreview, {
        params: {
          //...
        }
      })
      .then(response => {
        // optionally do something when the request was successful
      })
      .catch(error => {
        // console.log('Video loading error received: ' + error);
      })
      .finally(() => {
        // console.log('video loading finished');
        // hide video loading spinner...
      });
  }
}
</script>

1 like

Please or to participate in this conversation.