DEV Community

Cover image for Native lazy-loading - Why doesn't the video element have it?
Rob OLeary
Rob OLeary

Posted on • Originally published at roboleary.net

Native lazy-loading - Why doesn't the video element have it?

Lazy-loading is a strategy to identify resources that are not critical for the initial page load, and load them only when needed. It's a way to shorten the length of the critical rendering path, which translates into reduced page load times. It leads to faster websites.

We have the ability to lazy-load images and iframes in browsers now via the loading attribute.

<img src="cat.jpg" alt="felix the cat" loading="lazy">

<iframe src="video-player.html" title=".." loading="lazy"></iframe>
Enter fullscreen mode Exit fullscreen mode

Support for lazy-loading is good. Lazy-loading of images is supported by all major browsers now. However, it is still a bit patchy for iframes. Firefox does not support lazy-loading of iframes, and it is currently under an experimental flag in Safari. So, we are gettting there!

caniuse data for lazy attribute

It just made me wonder why the video element has been overlooked. Is it because most videos live on YouTube now and are embedded on websites via iframes?

I don't know but I certainly hope not! 😕🤞

When writing an article recently, I had a short screen recording to demo some functionality. I wanted to include it as autoplaying video, similar to where you might use an animated GIF. That's when I realised there is no native lazy loading for videos.

This nudged me towards converting the video into a WebP instead. Did you know that WebP supports animation the same as a GIF but with better compression?

You can use an online video to WEBP converter for this task.

I didn't dig that deeply into this topic as it was a bit of tangent from what I was doing! However, I did read a recent enough article (late 2019) by Google Devs on this topic, aptly titled "Lazy-loading video". They describe 2 distinct use cases for embedding videos that are handled differently. Let's take a look at these to understand the topic better.

Use Case 1: Videos where playback is initiated by the user

You have controls on the video, and it up the user to play the video.

You can specify the preload attribute on the video element to control loading. By providing preload="none", the browser should be prevented from loading the video data.

<!-- disable preloading -->
<video controls preload="none" width="300" poster="img/cover.jpg">
    <source src="files/sample.mp4" type="video/mp4">
</video>
Enter fullscreen mode Exit fullscreen mode

Do not include the autoplay attribute here as that may load the video, disregarding preload="none"!

The browser default behaviors with regard to preload are not set in stone, so being explicit with this is probably a good idea anyway.

On some browsers, the video will be have no background. You can make it look better by using the poster attribute to show a preview image.

When the user clicks the play button of the video, then the video will be loaded.

Use Case 2: A video acting as an animated GIF replacement

This was my use case.

Google recommends using a lazy loading library such vanilla-lazyload, or you can write your own JavaScript code.

To write your own code is not too long.

In the HTML, you do not put src attributes on the source elements, instead you stash the video URL in the data-src attribute. We mark video with a "lazy" class.

<video class="lazy" autoplay muted loop playsinline width="600" height="300" poster="cover.jpg">
  <source data-src="screen-recording.webm" type="video/webm">
  <source data-src="screen-recording.mp4" type="video/mp4">
</video>
Enter fullscreen mode Exit fullscreen mode

The JavaScript code uses the IntersectionObserver API to detect when the video element with the "lazy" class comes into view, and adds a src attribute to each source element with the video URL.

document.addEventListener("DOMContentLoaded", function() {
  var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));

  if ("IntersectionObserver" in window) {
    var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(video) {
        if (video.isIntersecting) {
          for (var source in video.target.children) {
            var videoSource = video.target.children[source];
            if (typeof videoSource.tagName === "string" && videoSource.tagName === "SOURCE") {
              videoSource.src = videoSource.dataset.src;
            }
          }

          video.target.load();
          video.target.classList.remove("lazy");
          lazyVideoObserver.unobserve(video.target);
        }
      });
    });

    lazyVideos.forEach(function(lazyVideo) {
      lazyVideoObserver.observe(lazyVideo);
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

Do you always want to do this for videos?

It looks to me like the addition of this functionality would be great. You could add preload="lazy"; or theloading attribute to match the other 2 elements.

Where is the suggestion box? 😄

Top comments (3)

Collapse
 
frenchcooc profile image
Corentin

Clearly, native support to lazy load iframes (especially YT's embed iframes) would be a big step forward. Video would just come next. I don't know why it's taking too much time though.

Collapse
 
robole profile image
Rob OLeary

bowie hovering video

Collapse
 
jonrandy profile image
Info Comment hidden by post author - thread only accessible via permalink
Jon Randy 🎖️

Autoplaying video... Ugh

Some comments have been hidden by the post's author - find out more