Looping video with fade-in using video.js

Avoid the awkward jitter of an "above-the-fold" autoplay video by hiding it behind a div until it begins to play. Includes fallback for users with javascript turned off.

The Great Unhinged.

Something genuinely wild & weird.
Check it out

Implementation notes

Last updated: 11/28/2024

As video becomes increasingly common on the internet, they're showing up increasingly "above-the-fold." But few things leave a worst first impression than a second or two (or more) of black before a video loads and begins playing. Videos can be designed to fade in, but for a looping video, the solution has to be implemented programmatically.

Enter video.js.

This simple implementation places a div over the top of a video & fades it in when the video playback begins. This is a more reliable method than basing a fade on a time delay, which may or may not coincide with the video's load time, given a user's connection.

Step 1 - Implementing video.js

Include the video.js CSS file in the page's <head>.

<link type="text/css" rel="stylesheet" href="https://vjs.zencdn.net/8.16.1/video-js.css" />

Include the video.js javascript file before the page's </body> tag.

<script type="text/javascript" src="https://vjs.zencdn.net/8.16.1/video.min.js"></script>

Step 2 - Adding the video

For hosting on Webflow, you can upload the video as a Background Video element & then view the page's source to find the URLs of the mp4, webm, & poster jpg files. But if you'd rather avoid Webflow's transcoding/compression, these files can be hosted anywhere. An image is also included as a fallback for browsers that do not support javascript. (This is also the content that will appear in the Webflow Designer.)

Include the following HTML in a Code Embed element.

<video-js 
	id="hero-video" 
	class="video-js vjs-fill" 
	autoplay 
	loop 
	muted 
	playsinline 
	preload="auto" 
	data-setup="{}"
	poster="https://cdn.prod.website-files.com/65aeaafb5fffb1901c9957c3%2F6748b8c97ef2ff19966093e0_background_sequence-1080-poster-00001.jpg"
	aria-hidden="true">
  <source src="https://cdn.prod.website-files.com/65aeaafb5fffb1901c9957c3%2F6748b8c97ef2ff19966093e0_background_sequence-1080-transcode.mp4" type="video/mp4" media="(min-width: 768px)">
  <source src="https://cdn.prod.website-files.com/65aeaafb5fffb1901c9957c3%2F6748b8c97ef2ff19966093e0_background_sequence-1080-transcode.webm" type="video/webm" media="(min-width: 768px)">
  <!-- fallback content for browsers that do not support javascript -->
  <img src="https://cdn.prod.website-files.com/65aeaafb5fffb1901c9957c3%2F6748b8c97ef2ff19966093e0_background_sequence-1080-poster-00001.jpg" class="object-fit-cover" loading="eager">
</video-js>

Step 3 - Make it responsive

Include some custom CSS to make the video & the poster image display using object-fit: cover;.

<style>
  #hero-video video {
    min-height: 100% !important;
    min-width: 100% !important;
    object-fit: cover !important;
  }
  #hero-video video[poster], video#hero-video[poster] {
    min-height: 100% !important;
    min-width: 100% !important;
    object-fit: cover !important;
  }
  #hero-video div.vjs-poster { 
    background-size: cover; 
  }
  .vjs-poster img {
    object-fit: cover;
  }
</style>

Some CSS will also need to be applied to the Code Embed element containing the HTML code. For this implementation, we're using Position Absolute - Full.

Step 4 - Add the cover div

We'll want to add a div absolutely positioned on top of the Code Embed element called Video Cover. We can set transform styles to this class to define the length & style of the fade out, & in the case that we want to add any interactivity to the video itself, we can set the click events to none. Inside that div, we'll create our cover content. For this exercise, it will be an absolutely positioned black div called Video Cover Color with a CSS loading animation inside. We'll set it to opacity: 0; by default so that if the user has javascript turned off, the this content won't hide the fallback image. We'll set that cover to opacity: 1; on page load if javascript is enabled.

Step 5 - Add the event listener for playback

When the video begins to play, this javascript will add a Fade Out class to Video Cover, setting the opacity to 0 & fading out according to the transitions styles applied to Video Cover. It should be included before the page's </body> tag, but after the video.js javascript file is referenced.

<script>
  document.addEventListener('DOMContentLoaded', () => {
    const player = videojs('hero-video');
    const videoCover = document.querySelector('.video-cover');

    // Event triggered when playback starts
    player.on('playing', () => {
      console.log('Video is now playing.');
      if (videoCover) {
        videoCover.classList.add('fade-out');
        console.log('.faded class added to .video-cover');
      } else {
        console.error('.video-cover element not found');
      }
    });
  });
</script>

Step 6 - Enable the fade

On page load, Webflow adds a class to the html element of the page, w-mod-js, that tells us that javascript is running in the browser. So when this class is present, we can set Video Cover Color opacity to 1. And we can set the combo class Video Cover Fade Out opacity to 0 to define the fade out. (Since this combo class won't be applied anywhere, it's best to add it as custom code so that it doesn't get removed when anyone cleans up the project's CSS.

This code should be applied to the page's <head>, as close to the top as possible to avoid a flicker of the Video Cover.

<style>
  .w-mod-js .video-cover-color {
	  opacity: 1;
  }
  .video-cover.fade-out {
  	opacity: 0;
  }
</style>

You're done!