Lazy Loading Youtube Videos

Fri Jan 11 2019

1 Background

The concept of lazy loading is a design pattern where you only download the required scripts/content when the client needs it rather than when the page is loaded. The most iconic example is the "infinite" feed that social media sites like Instagram use. Rather than dump a ton of content to the client at once, Instagram dynamically loads more content once you reach the end of the page. This allows websites to save bandwidth by only sending the client what they need when they need it.

When a youtube video is embedded into a website, it pulls a ton of scripts which are required to play the video. However, when you embed a youtube video on a blog post, most people are not going to watch that video. Even if the visitor wanted to watch that video, there is no reason why all the scripts should be loaded when you initially land on the page. Rather than embed the video normally, you can instead simply have a the thumbnail of the video with a play button on it. When the client clicks the play button, the youtube scripts are then loaded.

2 Performance Without Lazy Loading

Before Performance Score
Before Performance Score
Before Table Analysis of embedded youtube videos
Before Table Analysis of embedded youtube videos

A very basic blog post with two Youtube videos is 1.5 MB in size. That is a rather large page for a client to download and render. Notice that 65% of the content by size is from Youtube and 29 requests were from Google domains.

3 Performance with Lazy Loading

After Performance Score
After Performance Score
After Table Analysis of embedded youtube videos
After Table Analysis of embedded youtube videos

After lazy loading Youtube videos I was able to reduce the size of this blog post by nearly 1 MB. This also decreased the page load time by 75%. Additional tests show's that each embedded youtube video consumes around 500 KB. Using the lazy loading method you can slash that 500 KB to a mere 100 KB-- size of just the thumbnail.

4 Code Used

4.1 Javascript

This is some basic JavaScript code which selects all of the "youtube" embedded videos on our page and adds a play button handler. When the button is clicked, it updates the div to have an Iframe with the appropriate embedded youtube video. If you are already using jQuery, you could use that but, I did not since the goal of this is to optimize the page load time. The jQuery library is nearly 50 KB.

<script>
    ( function() {

        var youtube = document.querySelectorAll( ".youtube" );

        for (var i = 0; i < youtube.length; i++) {

            var source = "https://img.youtube.com/vi/"+ youtube[i].dataset.embed +"/sddefault.jpg";

            var image = new Image();
            image.src = source;
            image.addEventListener( "load", function() {
                youtube[ i ].appendChild( image );
            }( i ) );

            youtube[i].addEventListener( "click", function() {

                var iframe = document.createElement( "iframe" );

                iframe.setAttribute( "frameborder", "0" );
                iframe.setAttribute( "allowfullscreen", "" );
                iframe.setAttribute( "src", "https://www.youtube.com/embed/"+ this.dataset.embed +"?rel=0&showinfo=0&autoplay=1" );

                this.innerHTML = "";
                this.appendChild( iframe );
            } );
        };

    } )();
</script>

4.2 CSS

This CSS defines how the embedded youtube video looks before you click the play button.

.wrapper {
  max-width: 680px;
  margin: 60px auto;
  padding: 0 20px;
}

.youtube {
  background-color: #000;
  margin-bottom: 30px;
  position: relative;
  padding-top: 56.25%;
  overflow: hidden;
  cursor: pointer;
}
.youtube img {
  width: 100%;
  top: -16.82%;
  left: 0;
  opacity: 0.7;
}
.youtube .play-button {
  width: 90px;
  height: 60px;
  background-color: #333;
  box-shadow: 0 0 30px rgba( 0,0,0,0.6 );
  z-index: 1;
  opacity: 0.8;
  border-radius: 6px;
}
.youtube .play-button:before {
  content: "";
  border-style: solid;
  border-width: 15px 0 15px 26.0px;
  border-color: transparent transparent transparent #fff;
}
.youtube img,
.youtube .play-button {
  cursor: pointer;
}
.youtube img,
.youtube iframe,
.youtube .play-button,
.youtube .play-button:before {
  position: absolute;
}
.youtube .play-button,
.youtube .play-button:before {
  top: 50%;
  left: 50%;
  transform: translate3d( -50%, -50%, 0 );
}
.youtube iframe {
  height: 100%;
  width: 100%;
  top: 0;
  left: 0;
}

4.2.1 Example

4.3 HTML

Rather than use the embed code that youtube provides, you have to use the following code to integrate with the CSS and JS written. All you need from the youtube video is the ID which appears in the link associated with that video.

<div class="wrapper">
    <div class="youtube" data-embed="DoDaHmyIPvQ">
        <div class="play-button"></div>
    </div>
</div>

4.4 Node Templating Stuff

When witting blog posts in Markdown, it is not convenient to include 5 lines of HTML to embed a video. On my node server I wrote some code to convert a simpler "youtube tag" into the 5 lines of HTML shown above. This is completely optional.

//regular expression to detect custom youtube video tag
//<youtube src="" />
var re = /\<youtube .*?>/;

//result is a string which contains a HTML document

while (result.search(re) != -1)
{
    var ytid = result.substring(result.search(re) + 14, result.search(re)+ 11 + 14);
    var youtubeHTML = "<div class=\"wrapper\">\n" +
        "\t<div class=\"youtube\" data-embed=\"" +
         ytid +
        "\" />\n" +
        "\t\t<div class=\"play-button\"></div>\n" +
        "\t</div>\n" +
        "</div>\n";

    var original = "<youtube src=\"" + ytid + "\" />";

    result = result.split(original).join(youtubeHTML);
}