How to prevent YouTube js calls from slowing down page load
Asked Answered
V

11

23

I am using https://gtmetrix.com to diagnose issues with my page speed.

The page in question has one embedded YouTube video and GTMetrix is saying that this video's JS calls are slowing down page load speed.

This is the call being made:

<iframe width="640" height="360" src="https://www.youtube.com/embed/PgcokT0AWHo" frameborder="0" allowfullscreen></iframe>
Vicious answered 18/5, 2018 at 11:56 Comment(3)
Have you tried to defer the iframe loading by setting the src dynamically in window.onload or jQuery's ready?Footstool
@Footstool I am not using jQuery. But I am curious how I can use the window.onload. Is there an example of that? Is that something that happens in the header?Vicious
2022: web.dev/embed-best-practices - you can use lite youtube embed which is 224 faster and recommended.Dawdle
F
9

EDIT: as of October 2019, this is not working anymore (Thanks to @CpnCrunch for the update). For the cases where it's not impacting the user experience, you can apparently add a 1000ms timeout after the page load for it to take effect.

This is an example of attibuting the src dynamically on page load, using a pure JS solution. Using event listeners instead of window.onload because with the latter, only one event can be set and it would override any precedent window.onload event:

<iframe id="videoFrame" width="640" height="360" src="" frameborder="0" allowfullscreen></iframe>
<script>
function setVideoFrame(){
  document.getElementById('videoFrame').src = 'http://example.com/';
}
if (window.addEventListener)  // W3C DOM
  window.addEventListener('load', setVideoFrame, false);
else if (window.attachEvent) { // IE DOM
  window.attachEvent('onload', setVideoFrame);
}else{ //NO SUPPORT, lauching right now
  setVideoFrame();
}
</script>

Note that the script can be anywhere in the code, but in case of no support for event listeners (which is highly unlikely with nowadays browsers), the function is launched right away, so it should be at least after the iframe element.

Footstool answered 18/5, 2018 at 12:31 Comment(4)
That didn't seem to make the original issue go away :(Vicious
yes, replace the src by your own URL. It should work, maybe there is a cache system on your site that you have to empty?Footstool
This only helped a tiny bit. I still have these issues with the script: youtube.com/yts/jsbin/player_ias-vfl3_buxB/en_US/base.js (817.3KiB) youtube.com/yts/jsbin/www-embed-player-vflHvHVeu/… (84.6KiB) youtube.com/embed/PgcokT0AWHo (3.3KiB of inline JavaScript)Vicious
Unfortunately this doesn't work any more (Oct 2019) to improve google pagespeed. If you check pagespeed insights you'll see it makes no difference. For it to have an effect, I've found you need to put a delay of 1000ms after window onload before setting the src attribute, but that obviously creates a terrible user experience.Himself
A
7

I was having the same problem for a website I was doing and I stumbled upon this link How to "Lazy Load" embedded youtube videos

What it essentially does is, upon page load, it'll show a fake youtube player section (made out of css and your video image thumbnail) on the webpage and when the user clicks on it, it'll replace that fake player section with the Iframe and that's when the player will be loaded.

Code from the website

(function() {

  // get's all video wrapper divs
  var youtube = document.querySelectorAll(".youtube");

  // iterates through all the divs
  for (var i = 0; i < youtube.length; i++) {

    /* 
    gets the video id we mentioned in the data-embed attribute
    to generate image thumbnail urls, youtube has several
    resolutions.
    - mqdefault 320 x 180
    - hqdefault 480 x 360
    - sddefault - 640 x 480
    - maxresdefault - 1920 x 1080
    */
    var source = "https://img.youtube.com/vi/" + youtube[i].dataset.embed + "/sddefault.jpg";

    /*
    creates new image and sets the source attribute to the thumbnail
    url we generated above and sets it to load the image on page load
    */
    var image = new Image();
    image.src = source;
    image.addEventListener("load", function() {
      youtube[i].appendChild(image);
    }(i));

    /*
    below is where the magic happens, we attach click listeners to the 
    video embed divs and when clicked we create a new iframe and sets
    it inside the video wrapper div and only then will the js files
    associated with the embedded video be loaded
    */

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

})();
html {
  background-color: #f3f3f3;
}

.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;
}
<!-- (1) video wrapper,  data-embed contains ID of the Youtube video-->
<div class="youtube" data-embed="AqcjdkPMPJA">

  <!-- (2) the "play" button -->
  <div class="play-button"></div>

</div>
Artemisia answered 29/5, 2018 at 9:16 Comment(2)
Seems to require two clicks, which isn't ideal. Also, the snipper doesn't seem to be working any more.Himself
@Himself the embedded player is throwing an error because of the sandboxed env it's running in. Uncaught DOMException: Failed to read the 'cookie' property from 'Document': The document is sandboxed and lacks the 'allow-same-origin' flag.Artemisia
H
3

I couldn't find any workable solutions, so I wrote a javascript library to do this:

https://github.com/groupboard/ytdefer

It initially displays an image, and when the user clicks on it it then uses the YouTube Iframe API to load the video, and then start playing it (so that two clicks aren't required).

Himself answered 4/10, 2019 at 21:10 Comment(1)
absolutely loved the enthusiasm and idea! but... the implementation looks too big to me. smells like way too much code maintainance will be needed to keep this working over time... also, it might be harder to implement something like making it responsive... although it might facilitate my parallel quest to make it work offline or on a pwa. in any case, thanks for all the inspiration! 😘Motta
S
3

Just need to add a single line of code and its lazy loaded!

For example earlier it might be like :

  <iframe
      width="400"
      height="200"
      src="https://www.youtube.com/embed/sh0EGUheef8"
      frameborder="0"
      allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
      allowfullscreen
  ></iframe>;

Just need to change

src with srcdoc

srcdoc="<style>*{padding:0;margin:0;overflow:hidden}html,body{height:100%}img,span{position:absolute;width:100%;top:0;bottom:0;margin:auto}span{height:1.5em;text-align:center;font:48px/1.5 sans-serif;color:white;text-shadow:0 0 0.5em black}</style><a href=https://www.youtube.com/embed/sh0EGUheef8/?autoplay=1><img src=https://img.youtube.com/vi/sh0EGUheef8/hqdefault.jpg alt='AltTagContent'><span>▶</span></a>"

The trick is rooted in srcdoc, a feature of where you can put the entire contents of an HTML document in the attribute. It’s like inline styling but an inline-entire-documenting sort of thing.

The only problem is that the frame. The user will have to click it again. Since the first click was only to load the video. It still saves the junk that would get downloaded otherwise, and specially for mobile, those 500KB could hurt.

Sangsanger answered 13/9, 2020 at 15:10 Comment(0)
P
1

One possible approach is to not use the iframe directly but add it on some event listener, like click.

Have a look at this solution which I've been using on my website since long time (shameless promotion - Demo)

Here I show a preview image with a play button. When user clicks the image, the YouTube iframe is dynamically added to page, saving tons on bytes on page load. The code below is almost production ready and can be used as per your requirements.

if (document.querySelector("#videoPlayer")) {
  document.querySelector("#videoPlayer a").addEventListener("click", playVideo);
}
function playVideo() {
  var player = document.getElementById("videoPlayer");
  var id = player.getAttribute("data-id");
  player.classList.add("loaded");
  var src =
    "https://www.youtube.com/embed/" +
    id +
    "?autoplay=1&autohide=1&rel=0&modestbranding=1&showinfo=0&border=0&wmode=opaque&theme=light&iv_load_policy=3";
  var iframe =
    "<iframe width='100%' height='100%' src='" +
    src +
    "' scrolling='no frameborder='0' allowfullscreen></iframe>";
  player.innerHTML = iframe;
  return false;
}
#videoPlayer {
  background-color: #000;
  max-width: 100%;
  overflow: hidden;
  position: relative;
  cursor: pointer;
  height: 380px;
  width: 100%;
  margin: 1em auto;
}
#videoPlayer:after {
  content: attr(data-title);
  position: absolute;
  bottom: 0;
  left: 0;
  display: block;
  background: rgba(0, 0, 0, 0.5);
  width: 100%;
  max-height: 100px;
  text-align: left;
  padding: 1em;
  font-size: 1.2em;
  color: #fff;
  transition: opacity 0.7s ease-in-out;
}
#videoPlayer:hover:after {
  opacity: 0;
}
#videoPlayer .thumb {
  bottom: 0;
  display: block;
  left: 0;
  margin: auto;
  max-width: 100%;
  position: absolute;
  right: 0;
  top: 0;
  width: 100%;
  height: auto;
  opacity: 0.8;
  filter: alpha(opacity=80);
  transition: all 200ms ease-out;
  -webkit-transition: all 200ms ease-out;
}
#videoPlayer .thumb:hover {
  -webkit-transform: scale(1.2);
  transform: scale(1.2);
}
#videoPlayer .play {
  filter: alpha(opacity=90);
  opacity: 0.9;
  height: 97px;
  left: 50%;
  margin-left: -38px;
  margin-top: -38px;
  position: absolute;
  top: 50%;
  width: 136px;
  background: url("http://i.imgur.com/TxzC70f.png");
  background-repeat: no-repeat;
}
#videoPlayer.loaded:after {
  display: none;
}
<div id="videoPlayer" data-title="NBA 2K18 - Get Shook Trailer" data-id="lwBqitrE3ww"> <a title="Click to play video : NBA 2K18 - Get Shook Trailer"> <img class="thumb" alt="NBA 2K18 - Get Shook Trailer" src="https://1.bp.blogspot.com/-X4naiytBpyU/WZRt5d3P1iI/AAAAAAAADq8/y4IAHBmh39kqdwu8THECuObG3r9HIfa9wCLcBGAs/s800/nba-2k18-get-shook-trailer-hoopsvilla.jpg" /><span class="play"></span></a></div> 
</div>

Note: The demo above may not work due to cross-origin cookie issues. Please see the demo on codepen.

Possible improvements:

  1. Support multiple videos on same page. It currently allows only one video. It can be done easily with class based selectors.
Propinquity answered 26/5, 2018 at 18:43 Comment(0)
H
1

Look at given examples of using an iframe with src attribute and with dynamic src attribute with GTMatrix results.

<html>
<head>
    <title>-</title>
</head>
<body>
    <iframe id="myvideo" width="640" height="360" src="https://www.youtube.com/embed/PgcokT0AWHo" frameborder="0" allowfullscreen></iframe>
</body>
</html>

Here is the result of GTMatrix for above code.

enter image description here

Now check dynamic src code.

<html>
<head>
    <title>-</title>
    <script type="text/javascript">
        function test()
        {
            document.getElementById('myvideo').src = 'https://www.youtube.com/embed/PgcokT0AWHo';
        }
    </script>
</head>
<body onload="test();">
    <iframe id="myvideo" width="640" height="360" frameborder="0" allowfullscreen></iframe>
</body>
</html>

Now, look at the GTMatrix result after applying dynamic src to the iframe.

enter image description here

Hope these examples will help you to evaluate your problem.

Hadwin answered 29/5, 2018 at 12:38 Comment(1)
Unfortunately this doesn't work any more (Oct 2019) to improve google pagespeed. If you check pagespeed insights you'll see it makes no difference. For it to have an effect, I've found you need to put a delay of 1000ms after window onload before setting the src attribute, but that obviously creates a terrible user experience.Himself
P
1

Based on the response of https://mcmap.net/q/564702/-how-to-prevent-youtube-js-calls-from-slowing-down-page-load and with the 2000ms timeout changes I solved the issue in this way.

// Lazy Youtube.js
function setVideoFrames() {
    window.setTimeout(function () {
        let youtube_iframes = document.querySelectorAll('.lazy-youtube');

        if (youtube_iframes !== null) {
            for (let i = 0; i < youtube_iframes.length; i++) {
                youtube_iframes[i].src = youtube_iframes[i].getAttribute('data-src');
            }
        }
    }, 2000);
}

if (window.addEventListener)  // W3C DOM
    window.addEventListener('load', setVideoFrames, false);
else if (window.attachEvent) { // IE DOM
    window.attachEvent('onload', setVideoFrames);
} else { //NO SUPPORT, lauching right now
    setVideoFrames();
}

Then the regular html with the class lazy-youtube

<iframe width="1140" height="733"
        class="lazy-youtube"
        data-src="https://www.youtube.com/embed/{{ video-id }}?rel=0"
        frameborder="0"
        allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
        allowfullscreen title="Youtube Video"></iframe>
Platas answered 28/5, 2020 at 7:35 Comment(0)
V
0

You could replace:

<iframe src="https://www.youtube.com/embed/LcIytqkbdlo" height="180" width="320"></iframe>

with

<div class="youtube" id="LcIytqkbdlo" style="width: 320px; height: 180px;"></div> <script src="https://labnol.googlecode.com/files/youtube.js"></script>

as exemplified here: https://www.mainstreethost.com/blog/light-youtube-embeds-faster-page-load/

You could find another useful article which addresses your problem over here: https://mikeindustries.com/blog/archive/2007/06/widget-deployment-with-wedje

WEDJE creates a cross-platform, cross-browser defer by using the document object model (DOM) to append a div, create a script element, and then append the script element to the div, all with JavaScript. An example of the technique follows:

<script type="text/javascript"> // create div below
(function( ){document.write('<div id="wedje_div_example">Loading widget...<\/div>');
    s=document.createElement('script'); // create script element
    s.type="text/javascript"; // assign script to script element
    s.src="http://www.example.com/scripts/widget.js";
    // assign script s to div element
    setTimeout("document.getElementById('wedje_div_example').appendChild(s)",1);})( )
</script>

When these elements are linked together in this way, browsers appear to decouple the loading and execution of the attached JavaScript, making widget execution asynchronous! Here is the matching external JavaScript file, widget.js, which grabs the div we created earlier and loads an image:

        document.getElementById('wedje_div_example').innerHTML+='<img src="https://www.example.com/images/example.gif" width="60" height="60" />';
Vesperal answered 29/5, 2018 at 8:17 Comment(1)
The googlecode link isn't working any more, and the solution seems to require two clicks from the user which isn't ideal.Himself
H
0
<iframe width="640" height="360" src="" data-src="https://www.youtube.com/embed/PgcokT0AWHo" frameborder="0" allowfullscreen></iframe>

<script>
function init() {
    var vidDefer = document.getElementsByTagName('iframe');
    for (var i=0; i<vidDefer.length; i++) {
        if(vidDefer[i].getAttribute('data-src')) {
            vidDefer[i].setAttribute('src',vidDefer[i].getAttribute('data-src'));
        } 
    } 
}
window.onload = init;
</script> 
Husk answered 1/12, 2018 at 13:59 Comment(1)
Unfortunately this doesn't work any more (Oct 2019) to improve google pagespeed. If you check pagespeed insights you'll see it makes no difference. For it to have an effect, I've found you need to put a delay of 1000ms after window onload before setting the src attribute, but that obviously creates a terrible user experience.Himself
B
0

Iframe loads 4+ js files from youtube (of more than 1MB) you can use this approach overcome load time using Jquery.

HTML

<div class="youtube_video_iframe">

JavaScript

$window.on('load', function () {
    var youtube_video_iframe = `<iframe width="500" height="350" 
    src="https://www.youtube.com/embed/1gxcvLGKIRM" frameborder="0" allow="accelerometer; 
    autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`;
    $('.youtube_video_iframe').append(youtube_video_iframe);
});
Balthasar answered 19/9, 2020 at 18:18 Comment(0)
W
0

For reducing loading time of page which have youtube video embedded and making page load faster we don't need to add more javascript and make page loading slow. Its have simple solution that is adding loading attribute to tag Example:

<iframe loading="lazy" width="640" height="360" src="https://www.youtube.com/embed/PgcokT0AWHo" frameborder="0" allowfullscreen></iframe>

loading attribute with value lazy for tag enable lazy loading for iframe, which increase overall page performance even if there is multiple videos on web page.

Weakling answered 9/1 at 12:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.