Responsive Video Poster

Custom video placeholder with lazy load for better performance

Responsive Video Poster logo

Features

  • Full styling control of video placeholders
  • An image/picture is used for the poster which allows the use of src-set
  • Videos and embeds are lazy loaded to reduce page load
  • Embeds are prefetched on user interaction to improve performance
  • HTML Web Component version

Examples

Image

The default setup for the plugin with an image and a video.
The poster is the first frame of the video.

Embed

Embedded video from a third-party site e.g. Youtube, Vimeo, etc.
The poster is the middle frame of the video.

No image

The placeholder is styled instead of using an image.

Web component

Installation

$ npm install responsive-video-poster --save-dev

Usage

Import the files into your project, add the elements to your markup and initialize the plugin if using the JS version.

  • JS: node_modules/responsive-video-poster/src/scripts/responsive-video-poster.js
  • Web Component: node_modules/responsive-video-poster/src/scripts/responsive-video-poster-wc.js
  • CSS: node_modules/responsive-video-poster/src/styles/responsive-video-poster/responsive-video-poster.css
  • Compiled CSS: node_modules/responsive-video-poster/src/styles/responsive-video-poster.css

JavaScript

import { ResponsiveVideoPoster, ResponsiveVideoPosterAuto } from 'responsive-video-poster.js';

// Initialize a single element
const responsiveVideoPosterDefault = ResponsiveVideoPoster({ 
  selector: '#responsive-video-poster--default'
});

// Initialize all elements with default options, these can be overridden by reinitializing or by data attributes on the element.
ResponsiveVideoPosterAuto();

Web Component

import { ResponsiveVideoPoster } from 'responsive-video-poster-wc.js';

Options

The options can be set via initialization in the JS or by data attributes on the element with the prefix 'rvp'. The web component uses attributes without any prefix. Attributes will be given the highest priority.

<div class="responsive-video-poster" data-rvp-active-class="is-open">
<responsive-video-poster active-class="is-open">
Property Default Type Description
selector '.responsive-video-poster' String || Element Container element selector.
placeholderSelector '.rvp-placeholder' String Placeholder element selector.
posterSelector '.rvp-poster' String Poster element selector.
videoSelector '.rvp-video' String Video element selector.
animClass 'is-anim' String CSS class to transition the video placeholder between states.
activeClass 'is-active' String CSS class when placeholder is transitioned and the video is playing.
playDelay 'transition' Integer(ms) || 'transition' Delay playing the video by set time or wait for the placeholder transition to finish.
playDelayOffset 0 Integer(ms) Reduces playDelay to start loading the video before the placeholder has finished transitioning.
hideControlsOnLoad true Boolean Hide video controls while transitioning placeholder.
hideControlsOnFirstPlay false Boolean Hide video controls on first video play.
preConnections [] Array || Comma seperated list Domains to preconnect to for loading an embed. Youtube and Vimeo are automatically preconnected.

API

Property Type Description
instance.playVideo() Method Plays the video.
instance.getInfo() Object Returns the element and its properties.

Markup

<div class="responsive-video-poster">
                
  <button class="rvp-placeholder" aria-label="Play video">
    <span class="rvp-button" aria-hidden="true"><svg class="rvp-button-icon"> ... </svg></span>

    <img srcset="images/720.jpg 720w, images/1080.jpg 1080w" src="images/1080.jpg" class="rvp-poster" width="1080" height="608" loading="lazy">
  </button>

  <video src="videos/video.mp4" class="rvp-video" preload="none" width="1080" height="608"></video>

</div>

Web Component

As above but using a custom element.

<responsive-video-poster> ... </responsive-video-poster>

Performance optimizations

Responsive image techniques(srcset, sizes, source, etc) are used to load the most appropriate image. Native lazy loading is used on the responsive poster image to reduce initial page load. The plugin should also work normally with lazy load plugins.

The video element includes preload="metadata" or preload="none" so the video is not loaded until the user interacts with it. This will reduce the initial page load but may create lag in playback for some users. This can be removed if not needed.

The embedded video includes srcdoc="" so the video and third-party scripts are not loaded until the user interacts with it. The 'preConnections' option can be used to pass domains to start pre-connect connections for loading an embedded video. this happens when the user hovers/taps on the video using the pointerover event. For example Youtube could use ['https://www.youtube.com', 'https://www.google.com'].

Events

A 'playVideo' event is started once a user clicks the video placeholder. The event is ended once the placeholder has become inactive and the video starts playing.

document.querySelector('#responsive-video-poster--default').addEventListener('playVideo', (event) => { 
  console.log(`Action: ${event.detail.action}`);
});

You can also attach this to the document.

document.addEventListener('playVideo', (event) => { 
  console.log(`Target: ${event.target.matches('#responsive-video-poster--default')}`, `Action: ${event.detail.action}`);
});

Compatibility

Supports all modern browsers at the time of release.

Demo site

Clone the repo from Github and run the commands below.

$ npm install
$ npm run dev

Credits

Lazy loading of an embedded video by Arthur Corenzan. Lazy load third-party resources with facades. HTML Web Components by Jim Nielsen
Covverr videos of Lago di braies and Lofoten rocks. Youtube video of Tustan Karpaty mountains.