Faster YouTube Embeds with JavaScript

Alexis Ulrich
Tweet

We want our visitors to get our content as fast as possible, which means the content needs to be light, and with the minimum number of requests. But we want to keep users on our pages, and be entertained. This is where embedding videos enters the scene.

Videos illustrate our textual content, bring life to it, and are often served by a third party. What more to ask? Well, there’s a hidden price tag: Videos are slow and heavy to download, even when the visitor is not watching them.

One simple video on a page called through an innocuous iframe can add up to 6 HTTP requests and as much as 450kb of content. The solution I’m proposing in this article can reduce those numbers to 1 request and about 50kb per video, along with a few bytes of JavaScript (on top of the jQuery library if you don’t like vanilla flavor).

And you know what? This solution is not new. It was previously proposed by Amit Agarwal in April 2013.

So, What’s the Trick?

In Amit’s solution, the DOM is parsed by JavaScript on document load, and each call to a YouTube video (through a special div, not a regular iframe) is replaced by a thumbnail preview to which is attached the iframe when clicking on it.

This way, we get a nice preview thumbnail still served by a third-party server that’s a fraction of the weight of the full video player. The video player is loaded only when the video is actually viewed.

My Little Added Value

I rewrote Amit’s code in plain JavaScript and with jQuery for those interested. I kept the original comments in the code to keep it as understandable as possible. My version adds a new feature in the HTML5 data parameter that enables you to add any parameter to the YouTube URL to customize your video.

YouTube offers a list of parameters to show and hide controls, branding, and info, and to set the video quality or the starting frame of your video (among other things).

  • controls: set this to 0, and the layer controls are not displayed on the video player.
  • modestbranding: set this to 1, and the YouTube logo disappears from the control bar.
  • rel: set this to 0, and no related videos will be shown when the playback of the initial video ends.
  • showinfo: set this to 0, and the player will not display information like the video title and uploader before the video starts playing.
  • start: set this to a number of seconds, and the player begins playing the video from this time (or rather from the closest keyframe).
  • vq: set this to the wanted video quality, if supported (e.g.: hd720 when high quality is available)

When adding the YouTube iframe on the click event, some parameters are given values, namely autoplay (we want the video to start as soon as its thumbnail is clicked) and autohide (to hide the video progress bar and the player controls when no interaction is detected).

Supported YouTube Thumbnails

Each YouTube video comes with a list of generated images. You can access them through the URL http://img.youtube.com/vi/<youtube-video-id>/<youtube-thumbnail> (where img.youtube.com can even be shortened to i.ytimg.com, if you prefer).

The ones we are interested in are:

  • default.jpg (default version, 120px x 90px)
  • hqdefault.jpg (high quality version, 480px × 360px)
  • mqdefault.jpg (medium quality version, 320px × 180px)
  • sddefault.jpg (standard definition version, 640px × 480px)
  • maxresdefault.jpg (maximum resolution version, 1.280px × 720px)

As an example, see this URL, which you can use to play around with the values to see the different image options.

In the following code, we use the sddefault.jpg thumbnail. Depending on your needs and the capabilities of the user’s screen, this can be replaced by any of the listed supported formats.

The HTML

The HTML code sets the YouTube video ID, styles the video size (width and height), and lists the Youtube URL parameters if needed.

<div class="youtube"
     id="fsrJWUVoXeM" 
     style="width: 500px; height: 281px;">
</div>

<div class="youtube" 
     id="lR4tJr7sMPM" 
     data-params="modestbranding=1&showinfo=0&controls=0&vq=hd720"
     style="width: 640px; height: 360px;">
</div>

The CSS

In the two videos used as examples, the picture ratio is 16:9, which returns the sddefault.jpg thumbnail with horizontal black bands. To hide them when showing this thumbnail, the background-position property is set with the center value, while setting the picture size inlined in the HTML div tag, like style="width:500px;height:281px;". This way, it is possible to show different video sizes on the same page.

The play icon, which tells the users the content is not a mere picture, is added in a layer on top of the thumbnail with an opacity transition to highlight it. We are using a data URI base64-encoded png here (from IconFinder), which saves an HTTP request and works down to IE8.

.youtube {
    background-position: center;
    background-repeat: no-repeat;
    position: relative;
    display: inline-block;
    overflow: hidden;
    transition: all 200ms ease-out;
    cursor: pointer;
}

.youtube .play {
    background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAERklEQVR4nOWbTWhcVRTHb1IJVoxGtNCNdal2JYJReC6GWuO83PM/59yUS3FRFARdFlwYP1CfiojQWt36sRCUurRIdVFXIn41lAoVdRGrG1M01YpKrWjiYmaSl8ybZJL3cd+YA//NLObd3++eO8x79z5jSq5Gw+8kov0AP8vMR5l1BtBZQM4B8ks75wCdZdYZZj5qLZ4hov2Nht9Z9vhKKSIaB/gI4M4w62KeAO6Mte4lYOq20FxrlqqOibhHmeWbvNC9ZfDX1mLae391aN6limO/gwgvAPJbWeAZuSDingdwXTBw7/0IsyaA/Fkh+KqOkD+YNfHej1QKD+y7iVlOhgLvFqFfNJvNGyuBJ+KDAF8MDd0tgS8y64OlgSdJMsysL4cG7SOHkyQZLhTee7+d2R2rAVy/S+Jd7/32ouBHAP4gNNRGQyTHc/84NhqNywZp5rvjjnnvt21aABFeCQ+RLwAf2hQ8s7sv9OCLk6AHNgQvIrvbfzKCD76g/O6cu7lf/iER/aQGgy448pExZmhdegAPhR9sObFWH1gT3lp7DaA/5bkIgJhZPgsNmz02novj+KqeApj1ubwXWe4kdyeznAgNvTpE/HQmvKqOMeuFogTUVQSRno+iaLRLAJF7uIgL9O4ubgL8aWgB7S44mNX+35YpICUiAvS9sBLkq1WzT+NFffl6AuoiApi6NT37h6sWkBIRZGkQ8YtLgyji6e1mBYTqCEBPG2Naz+0BWQgtoGoRgCzEsd9hAN1X5BfnFZASUfrSAFQNsyZ1FJASUVpHiLinDJG8U2cBZYogkrcNs5waBAGdstbeU9zdqpw0gPwwSAI6VUxHyFlDpOcHUUBBIuYNs14aZAE5RVwyzPr3/0EAEY0TyfGNjBWQvwZ +CTSbehfAH29mrID8bET0+0EUkAd8WYDOmqJ3ecsG30yr9wqRfm6Y+a1BEFDEjHfHvWmY9ck6CygHvBVr8Xhtb4ZE5HZA3y8DvBNA1TjnrmXWf+sioMwZX5V/VHXMGGMMoKdDCxCRvRWBdzKzdHEO+EisilbPyopHYqp6S9UCAsz4iojI7hUDAtyXVQgIDd6KnOoaWNkbI6FaPSuZGyMArsi7MZoloB4zviI/Nhr3X95jltwTRQmoIfgisy5ai+me67OI7fE4nrqjrqfK1t0eby0FPRB6oGVlchL3rgnfrq19RKbVBdhV9IOSwJmfmJi4vi/4ThERitwyCxVAFqydshuCX5awhQ9KtmuIWd8IDZED/nXT77rvVVv6sHRKwjYi91poqP7Dr+Y6JJ1VSZIMA3wkPNy6bX+o8Bcm0sXMdwM8Fxo0A3xORPaWBp6uPXsmbxCRD0NDL0dOANhVCXy6iAjMcjbcrMt3RITKwdMVRdFo+y5yvkL4eWZ+zHt/ZVD4dEVRNGotpst+dZZZH8k86lqn2pIvT/eqrNfn2xuyqYPZ8mv7s8pfn/8Pybm4TIjanscAAAAASUVORK5CYII=") no-repeat center center;
    background-size: 64px 64px;
    position: absolute;
    height: 100%;
    width: 100%;
    opacity: .8;
    filter: alpha(opacity=80);
    transition: all 0.2s ease-out;
}

.youtube .play:hover {
    opacity: 1;
    filter: alpha(opacity=100);
}

Vanilla JavaScript Implementation

With no dependency and the fastest implementation, the vanilla JavaScript version uses the smallest DOM ready code I could find.

Browser differences still have to be taken into account, like the lack of support for the getElementsByClassName method in IE8.

'use strict';
function r(f){/in/.test(document.readyState)?setTimeout('r('+f+')',9):f()}
r(function(){
    if (!document.getElementsByClassName) {
        // IE8 support
        var getElementsByClassName = function(node, classname) {
            var a = [];
            var re = new RegExp('(^| )'+classname+'( |$)');
            var els = node.getElementsByTagName("*");
            for(var i=0,j=els.length; i<j; i++)
                if(re.test(els[i].className))a.push(els[i]);
            return a;
        }
        var videos = getElementsByClassName(document.body,"youtube");
    } else {
        var videos = document.getElementsByClassName("youtube");
    }

    var nb_videos = videos.length;
    for (var i=0; i<nb_videos; i++) {
        // Based on the YouTube ID, we can easily find the thumbnail image
        videos[i].style.backgroundImage = 'url(http://i.ytimg.com/vi/' + videos[i].id + '/sddefault.jpg)';

        // Overlay the Play icon to make it look like a video player
        var play = document.createElement("div");
        play.setAttribute("class","play");
        videos[i].appendChild(play);

        videos[i].onclick = function() {
            // Create an iFrame with autoplay set to true
            var iframe = document.createElement("iframe");
            var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
            if (this.getAttribute("data-params")) iframe_url+='&'+this.getAttribute("data-params");
            iframe.setAttribute("src",iframe_url);
            iframe.setAttribute("frameborder",'0');

            // The height and width of the iFrame should be the same as parent
            iframe.style.width  = this.style.width;
            iframe.style.height = this.style.height;

            // Replace the YouTube thumbnail with YouTube Player
            this.parentNode.replaceChild(iframe, this);
        }
    }
});

And here is a demo of the code in action:

See the Pen YouTube Embed Optimized with JavaScript by SitePoint (@SitePoint) on CodePen.

The jQuery Implementation

While being more expressive to my eyes and with wider browser support, the jQuery implementation comes with the price of the whole jQuery library and slightly slower performance.

"use strict";
$(function() {
    $(".youtube").each(function() {
        // Based on the YouTube ID, we can easily find the thumbnail image
        $(this).css('background-image', 'url(http://i.ytimg.com/vi/' + this.id + '/sddefault.jpg)');
    
        // Overlay the Play icon to make it look like a video player
        $(this).append($('<div/>', {'class': 'play'}));
    
        $(document).delegate('#'+this.id, 'click', function() {
            // Create an iFrame with autoplay set to true
            var iframe_url = "https://www.youtube.com/embed/" + this.id + "?autoplay=1&autohide=1";
            if ($(this).data('params')) iframe_url+='&'+$(this).data('params');
    
            // The height and width of the iFrame should be the same as parent
            var iframe = $('<iframe/>', {'frameborder': '0', 'src': iframe_url, 'width': $(this).width(), 'height': $(this).height() })
    
            // Replace the YouTube thumbnail with YouTube HTML5 Player
            $(this).replaceWith(iframe);
        });
    });
 });

And here is a demo of the jQuery version:

See the Pen YouTube Embeds Optimized (jQuery Edition) by SitePoint (@SitePoint) on CodePen.

The Final Results

Now, let’s talk about what we gain in real-world situations.

This solution was implemented on this page, which is an article that contains 3 embedded YouTube videos. Here are the results:

  • Before implementing that solution, we had 20 HTTP requests and 636.2kb of content downloaded, which took 2.22s (3.59s on load).
  • Once implemented, we went down to 17 requests, 370.7kb, and 1.05s (onload: 733ms).
  • This means 15% fewer requests, and a web page that’s 41% lighter and 52% faster (onload: 80% faster).

The results are quite good even with only one embedded YouTube video, like on this next page, which is our next example. Here are the results for the single-video page:

  • Before implementing the solution we had 20 HTTP requests and 684.4kb of content downloaded, which took 2.13s (2.14s onload).
  • Once implemented, we went down to 17 requests, 322.4kb, and 1.24s (onload: 975ms).
  • This means we ended up with 15% fewer requests, and a web page that’s 53% lighter and 42% faster (onload: 54% faster).

Conclusion

In closing, I think we can all agree that cutting down on page weight from between 40 to 50% is worth that little piece of code, don’t you think?

If you have any thoughts on the code, or would like to improve it, feel free to fork the code on CodePen or share your views in the comments.

This article is also available in French, Spanish and Portuguese.

Free JavaScript: Novice to Ninja Sample

Get a free 32-page chapter of JavaScript: Novice to Ninja

  • http://blog.piotrnalepa.pl/ Piotr Nalepa

    Nice article. I didn’t know about the thumbnails …

  • http://johngraham.mr John Graham

    Thanks for this, I tried it out on my site today. I put another little twist on it by running the iframe through fitvids.js when it’s added to the DOM.

    • http://www.mancko.com Alexis

      That’s a good idea. Maybe you could fork the CodePen to show us how you did it, as you can link external javascript resources to it.

  • Prashant Palikhe

    Why is the image embedded through JS instead of through html markup. I think within the img tags, the request to the thumbnails would be dispatched earlier, improving the load time. Unless the images are being lazy loaded on-scroll or something similar, which is not the case here.

    • LouisLazaris

      I was discussing with Alexis the possibility of using a class on the HTML to determine the video size and quality of the image. So in that case, you’d have to use JS, because he’s constructing the URL to the image on the fly. In this case, you’re probably right that if you’re only going to load one image and not change it, then it could be done right in the HTML. Unless I’m missing something.

      • http://www.mancko.com Alexis

        You are right. If we had set an image tag within the div directly in HTML, the picture would have been displayed earlier, and the perceived loading time would have been shorter.
        As Louis said, for the sake of genericity, everything is performed in the javascript code.
        However, as the image size is hard-coded in the inlined CSS, which is not a perfect solution, it allows us to define our own image size independently of YouTube preset thumbnails.
        As a side-effect, the lazy loading is de facto handled by javascript (as long as it is run after page load).

  • Mike

    Too bad it’s not responsive.

    • http://www.mancko.com Alexis

      You should give an eye to John Graham’s solution in the comments: he has created a CodePen using FitVids.js to handle the responsiveness.

  • RidleyO

    How difficult would it be to do this for an embedded playlist (as opposed to an individual video)?

  • http://www.mancko.com Alexis

    Your plugin is really interesting. Definitively a way to go as you can white label the whole YouTube embed player, even though you download the player code whether your visitor is watching the video or not.

  • http://www.mancko.com Alexis

    Thank you for that impressive solution.

  • Iza

    Lighter is better, but I can’t get to the play button using the keyboard. If these videos can only be played with a mouse, this will be a big issue for keyboard and some assistive technology users. I’m not a developer, so can’t suggest a solution, but I hope you can, Alexis!

  • http://www.echteinfach.tv/ Echt Einfach TV

    How I wished the guys from youtube would implement such an embed option by default to save the entire internet world zettabytes of server loads and trillions of hours of waiting time… thanks for the article. That made me dive into “lazy load youtube plugins” for jquery ;-)

  • Mike Badgley

    Any particular reason why you wouldn’t just go with the YouTube iFrame API? Otherwise this sort of feels like we’re reinventing the wheel…

    • http://www.mancko.com Alexis

      I have not used that API yet (here’s a link to its documentation: https://developers.google.com/youtube/iframe_api_reference ), but what I can see at a quick glance is that even though the iframe code download can be made asynchronous, the code is downloaded nevertheless, which can be a burden for a mobile device on a limited data plan.
      So it does not seem possible to download only a photo from the video and postpone the full player download until the user actually clicks on it. But I’d be happy you prove me wrong with a working example, though.

      I prefer to play safe with that API to be the less dependent possible on it, both for maintanability issues (there should be only minimal testing in case of an upgrade), and to be able to adapt the code to other providers too (Vimeo, Dailymotion or others).
      Apart from that point, the API offers a way deeper control over the player (volume, playlists…), which can be useful if you want to manage more interaction with the player.

      • Mike Badgley

        Yah good point, I guess it really all comes down to what you what or need. If you need minimal to no control over the video, than your approach is definitely the quickest-and-easiest to go with.

        Thanks for the reply! :)

  • චාම‍ර

    Hi Alexis
    Thank you for this nice article..
    How to Change the Iframe to previous state.(like a image link)after stop the video