Lazy Loading Videos on Scroll with Vanilla JS and Intersection Observers
Tags: javascript
Published: 2023-08-02 09:27:16

---
title: Lazy Loading Videos on Scroll with Vanilla JS and Intersection Observers
published: true
date: 2023-08-04 11:59:04 UTC
tags: JS
canonical_url: https://robertmarshall.dev/blog/lazy-loading-videos-on-scroll-with-vanilla-js-and-intersection-observers/
---

--------

This article was originally posted (and is more up to date) at https://robertmarshall.dev/blog/lazy-loading-videos-on-scroll-with-vanilla-js-and-intersection-observers/

--------

As frontend developers, we often encounter the challenge of optimizing website performance. One powerful technique to achieve this is lazy loading.

Lazy loading allows us to load assets, such as images or videos, only when they are needed, reducing initial page load times and conserving bandwidth.

I have written pieces on using [React and Lazy Loading](https://robertmarshall.dev/blog/lazy-load-vimeo-video-iframe-show-on-scroll/), but sometimes Vanilla JS is needed.

In this article, we’ll focus on lazy loading videos using the Intersection Observer API. By leveraging this built-in browser feature, we can efficiently detect when videos come into the user’s viewport and load them on demand.

**TLDR**

Take a look at this CodeSandbox for a working example of [how to load a video on scroll](https://codesandbox.io/s/lazy-loading-videos-on-scroll-with-vanilla-js-and-intersection-observers-52xc2z).

_ **Update** _

_I recently wrote a piece on deferring the video until a user clicks. This is a more effective solution than waiting until scroll. It is about YouTube but could be refactored to work with most providers._

[_robertmarshall.dev/blog/on-click-lazy-load-video-iframe-in-react/_](https://robertmarshall.dev/blog/on-click-lazy-load-video-iframe-in-react/)

## Setting up the HTML Structure

Before we delve into the JavaScript code, let’s understand the HTML structure we’ll be working with.

In the example, we’ll have multiple video elements, each representing a different video to be loaded on scroll.

```html
<div>
  <div
    data-video-url="https://player.vimeo.com/video/588259728?h=09704145bb&dnt=1&app_id=122963&autoplay=1&muted=1"
    data-video-title="This is an iframe title"
    class="video-wrap"
    style="
      overflow: hidden;
      padding-top: 56.25%;
      position: relative;
      width: 100%;
    "
  ></div>
  <div
    data-video-url="https://player.vimeo.com/video/588259728?h=09704145bb&dnt=1&app_id=122963&autoplay=1&muted=1"
    class="video-wrap"
    style="
      overflow: hidden;
      padding-top: 56.25%;
      position: relative;
      width: 100%;
    "
  ></div>
</div>
```

Each `div` element contains styling to ensure the video’s correct aspect ratio and prevent page jumps when the video finally loads. The first `div` includes `data-video-url` and `data-video-title` attributes, which we will later grab to inject into the iframe.

## Section 2: The Intersection Observer API

Now to cover the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). This is a built-in browser feature that allows us to observe changes in the intersection of elements with a viewport or a specific container.

The Intersection Observer is perfect for our lazy loading implementation because it can detect when a video element comes into view, signalling the browser to load the video.

## Section 3: The JavaScript Implementation

First, we need to wait for the DOM to be fully loaded before starting our JavaScript implementation. We’ll use the `DOMContentLoaded` event to ensure our script runs at the right time.

```javascript
if (document.readyState !== "loading") {
  initVimeo();
} else {
  document.addEventListener("DOMContentLoaded", initVimeo);
}

```

Next, let’s define the `initVimeo()` function, which will be the starting point of the lazy loading implementation. It will use the querySelectorAll method to select all video elements with the `data-video-url` attribute.

```javascript
function initVimeo() {
  const videoWraps = document.querySelectorAll(".video-wrap[data-video-url]");
  // More code will be added here later
}
```

## Section 4: Implementing Lazy Loading

Inside the `initVimeo()` function the Intersection Observer will check when each video element comes into view. If it’s the first time the element is visible (not yet loaded), it will to load the video dynamically.

```javascript
function initVimeo() {
  const videoWraps = document.querySelectorAll(".video-wrap[data-video-url]");
  const videoStateMap = new Map();

  const observer = new IntersectionObserver((entries) => {
    entries.forEach(({ target, isIntersecting }) => {
      if (isIntersecting && !videoStateMap.get(target)) {
        const videoUrl = target.getAttribute("data-video-url");
        const videoTitle = target.getAttribute("data-video-title");

        const iframe = document.createElement("iframe");
        iframe.src = videoUrl;
        iframe.frameBorder = "0";
        iframe.allow = "autoplay; fullscreen; picture-in-picture";
        iframe.allowfullscreen = "";

        if (videoTitle) {
          iframe.title = videoTitle;
        }

        iframe.style = "position: absolute; inset: 0; width: 100%; height: 100%";

        target.appendChild(iframe);

        videoStateMap.set(target, true);
      }
    });
  });

  videoWraps.forEach((videoWrap) => {
    videoStateMap.set(videoWrap, false);
    observer.observe(videoWrap);
  });
}
```

By leveraging the Intersection Observer API, we’ve successfully implemented lazy loading for videos, ensuring they load only when they are about to be displayed on the user’s screen. This optimization results in faster page load times and a smoother user experience.

To see a working version of this code, [take a look at the Code Sandbox](https://codesandbox.io/s/lazy-loading-videos-on-scroll-with-vanilla-js-and-intersection-observers-52xc2z).

You can find me on [Twitter](https://twitter.com/robertmars) if you have any questions.