Play Video on Hover (Lazy)

Category: Video & Audio. Last updated: Aug 20, 2025

Code

index.html
html
<div data-video-on-hover="not-active" data-video-src="https://osmo.b-cdn.net/resource-video/whatsapp-modal-1440x900.mp4" class="video-card">
  <div class="video-card-visual">
    <img src="https://cdn.prod.website-files.com/68493e75b2d7f03a5fa360af/68495579840e207fe2517a41_video-card-1.avif" class="video-card-visual__img">
    <video muted loop webkit-playsinline playsinline src="" class="video-card-visual__video"></video>
  </div>
  <span class="video-card-title">WhatsApp Modal</span>
</div>
styles.css
css
.video-card {
  gap: 1em;
  background-color: #f0f0f0;
  border-radius: .75em;
  flex-flow: column;
  width: 22em;
  padding: .75em .75em 1.125em;
  transition: background-color .2s;
  display: flex;
}

.video-card:hover {
  background-color: #fff;
}

.video-card-visual {
  aspect-ratio: 1.6;
  border-radius: .5em;
  width: 100%;
  position: relative;
  overflow: hidden;
}

.video-card-visual__img {
  object-fit: cover;
  width: 100%;
  height: 100%;
}

.video-card-visual__video {
  opacity: 0;
  width: 100%;
  height: 100%;
  padding: 0;
  transition: opacity .2s;
  position: absolute;
  inset: 0%;
}

.video-card-title{
  padding-left: 0.75em;
}

/* ———— If video is active and hovered, set it to opacity 1 ———— */
[data-video-on-hover="active"] video{ opacity: 1; }

/* ———— If video is active and hovered, set it to opacity 1 ———— */
[data-video-on-hover="active"] video{ opacity: 1; }
script.js
javascript
function initPlayVideoHover() {
  const wrappers = document.querySelectorAll('[data-video-on-hover]');

  wrappers.forEach(wrapper => {
    const video = wrapper.querySelector('video');
    const src = wrapper.getAttribute('data-video-src') || '';
    if (!video || !src) return;

    wrapper.addEventListener('mouseenter', () => {
      if (!video.getAttribute('src')) {
        video.setAttribute('src', src);
      }
      wrapper.dataset.videoOnHover = 'active';
      video.play().catch(err => {
        console.warn('play on hover is blocked:', err);
      });
    });

    wrapper.addEventListener('mouseleave', () => {
      wrapper.dataset.videoOnHover = 'not-active';
      setTimeout(() => {
        video.pause();
        video.currentTime = 0;
      }, 200);
    });
  });
}

document.addEventListener("DOMContentLoaded", () => {
  initPlayVideoHover();
});

Guide

Documentation

A lightweight, dependency-free component that displays a placeholder image and seamlessly transitions to a video on hover. Perfect for dynamic previews and interactive components without slowing down your page speed. Videos are created on-demand, by only fetching and serving when a user hovers over the card.

HTML Structure

Add data-video-on-hover to any wrapper element of your HTML5 video element. This is the attribute that the code will target for hover events. We will also toggle an active or not-active value on this attribute to control the opacity of a placeholder image.To that same wrapper element add an attribute of data-video-src and include a link to your (mp4) video in the attribute value. Once a user hovers the wrapper, we will load the video src dynamically and play!To our video element inside, make sure to include the following attributes:muted: Required for autoplay browser policy.loop: Optional of course.playsinline / webkit-playsinline: Ensure inline mobile playback.We will define opacity: 0 to our video in CSS.

JavaScript

Hover InOn first hover, sets video.src from data-video-src. This only needs to happen once, on every hover after that, we can simply call play()Updates data-video-on-hover to "active". In our CSS we use this to set our video to opacity: 1Calls video.play(), silently catching any rejection.Hover OutUpdates data-video-on-hover to "not-active". This will hide our video again.After a 200 ms delay, pauses playback and resets currentTime to 0. That's it! A simple, no-dependency way to add engaging video previews to your cards.

Hosting your own videos

We recommend checking the video attached to this resource, in which Ilja explains step-by-step how to setup your own video hosting at Bunny.net. It takes a couple of minutes to set up a super fast, cheap, and reliable service for yourself and/or clients. Always make sure to compress your videos before uploading, by using a website like videosmaller.com or a tool like Handbrake.