Native lazy-loading - Why doesn't the video element have it?
Tags: webdev, performance, html, javascript
Published: 2022-03-28 13:09:43

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](https://developer.mozilla.org/en-US/docs/Web/Performance/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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading).

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

<iframe src="video-player.html" title=".." loading="lazy"></iframe>
```

[Support for lazy-loading is good](https://caniuse.com/?search=loading). 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!

<img src="https://caniuse.bitsofco.de/static/v1/loading-lazy-attr-1648472837831.png" alt="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](https://en.wikipedia.org/wiki/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](https://www.onlineconverter.com/video-to-webp) 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"](https://web.dev/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](https://developer.mozilla.org/docs/Web/HTML/Element/video#attr-preload) on the `video` element to control loading. By providing `preload="none"`, the browser should be prevented from loading the video data.

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

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](https://github.com/verlok/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.

```html
<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>
```

The JavaScript code uses the [`IntersectionObserver` API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_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.

```javascript
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);
    });
  }
});
```

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 the`loading` attribute to match the other 2 elements.

Where is the suggestion box? 😄