Blog (my web musings)

Find out what's interesting me, get tips, advice, and code gems that don't fit elsewhere.


Search Blog


Through the course of a web training session, during development for a school learning zone, I was asked "How do we put YouTube videos on a web page?". So, I went over the steps of copying and pasting a video's embed code, the trainee repeated, all questions were answered, and we finished the meeting with smiles and a cheery wave.

About a week later, I decided to review their progress to see how they were getting on. I clicked on page one. It was clean and orderly; they'd obviously paid attention and I was happy to see all the learning materials they'd uploaded presented beautifully on the page. Then I clicked on page two. The URL changed in the address bar. The banner refreshed. But then the page stopped dead. My mouse wouldn't move and the scrollbar wouldn't budge. The page was totally unresponsive.

After a minute, once the fans had stopped whirring in my computer, I finally managed to scroll down the page in sporadic fits, using the arrows on my keyboard. I spotted the scrollbar retracting quickly into the top corner as a tiny little square but continued to press on. The thing I was fighting against turned out to be the effects of 71 embedded YouTube videos (on top of umpteen other file links and image resources). You read that right: Seventy one!

Here's a screencap of how that looked in the Network tab in Chrome's developer console;

The bottom line (literally) reports over 1200 requests, and 40MB of transferred data downloaded. I blanched a little.

The Problem

Everything added to a web page will increase the overall weight and load time, and for YouTube embedded videos, that means extra scripts, stylesheets, and placeholder images, and a shed-load of overhead from the YouTube servers. The page-in-question had a whopping 71 videos, which meant that all those extra resources were multiplied 70 times over, and were all imposed upfront on a web visitor, regardless of whether they'd chosen to watch a video or not.

I realised that I'd not specifically told the trainee to put a cap on the total number of videos they were embedding. I honestly hadn't expected them to be so lavish. So, rather than force them to undo all of their hard work, disrupt their workflow, and potentially erode their confidence in IT systems, I decided to think about what I could do to fix the problem from my side.

A Solution

While Googling for advice on dealing with multiple YouTube embeds, I came across this How to "Lazy Load" Embedded YouTube Videos script by Thoriq Firdaus. Please take a few minutes to read the article. The idea is simple, yet effective, but I didn't relish the thought of manually editing the markup of those 71 video embeds... So I automated the process with JavaScript;

function stripYouTubeEmbed() {
    var embed = document.querySelectorAll('iframe[src^="https://www.youtube.com/embed/"]'), length = embed.length, i, url, id;
    for (i = 0; i < length; ++i) {
        url = embed[i].getAttribute('src');
        id = url.substr(url.lastIndexOf('/') + 1); // get video id from youtube embed url
        embed[i].outerHTML = '<div class="youtube-wrap"><div class="youtube" data-embed="'+id+'"><div class="youtube-play"><\/div><\/div><\/div>';
        }
    }
stripYouTubeEmbed();

The stripYouTubeEmbed() JavaScript function strips out YouTube embedded iframes, before any additional resources get chance to load, and replaces them with wrapper divs holding a "youtube" class, and a "data-embed" attribute containing the YouTube video ID, which is derived from the iframe's original "src". The JavaScript-modified HTML looks like this;

<div class="youtube-wrap">
    <div class="youtube" data-embed="leVxy7cjZMU">
        <div class="youtube-play"></div>
    </div>
</div>

This, in conjunction with Thoriq Firdaus' script, improved the performance of the page significantly;

Now there are 158 requests, and 3.6MB of data transferred, which is great, but I wanted to do better; 3.6MB is still a lot of extra overhead for users who don't get past the first few videos. So, I headed for my Add Class to Target-Element when Trigger-Element is Scrolled into View script to see if I could use it to load resources only when scrolled-to.

The "youtube" class from the newly modified markup, is passed to the elementFromTop() JavaScript function, which adds a "yt-load" class when the scroll distance is met;

<div class="youtube-wrap">
    <div class="youtube yt-load" data-embed="leVxy7cjZMU">
        <div class="youtube-play"></div>
    </div>
</div>

The LazyLoadYouTubeEmbed() function (adapted from Thoriq Firdaus' How to "Lazy Load" Embedded YouTube Videos script) kicks in at this point, looking for elements with this "yt-load" class, and the "data-embed" attribute, to load in the associated placeholder image from YouTube;

<div class="youtube-wrap">
    <div class="youtube yt-load" data-embed="leVxy7cjZMU">
        <div class="youtube-play"></div>
        <img src="https://img.youtube.com/vi/leVxy7cjZMU/sddefault.jpg">
    </div>
</div>

The previously stripped iframe code is returned, and replaces the placeholder image when clicked;

<div class="youtube-wrap">
    <div class="youtube yt-loaded yt-load" data-embed="leVxy7cjZMU">
        <iframe frameborder="0" allowfullscreen="" src="https://www.youtube.com/embed/leVxy7cjZMU?rel=0&amp;showinfo=0&amp;autoplay=1"></iframe>
    </div>
</div>

The last thing the LazyLoadYouTubeEmbed() function does is add a "yt-loaded" class, which it uses to exclude a video embed from the next round of executions, thanks to the :not() CSS selector used like this in the first line: .yt-load:not(.yt-loaded)

This is how the final page looks;

Only 86 requests, and 24kb of transferred data! I'm very happy with that. See a full working example below.

Defer YouTube Loading until Scrolled-To (Lazy-Loading):

Demo