Lazy Load youtube video from iframe_api

It’s done here successfully.
https://jsfiddle.net/bs7czfpx/

I’m trying to have it work with this code.
https://jsfiddle.net/182tL0f5/

I wasn’t able to figure out how to do this.

Either id or class, not sure which.

<div class="placeholder">
      <svg class="play " width="100%" height="100%" viewBox="0 0 64 64">
        <path d="M25.6,46.4L44.8,32L25.6,17.6V46.4z M32,0C14.3,0,0,14.3,0,32s14.3,32,32,32s32-14.3,32-32S49.7,0,32,0z
              M32,57.6C17.9,57.6,6.4,46.1,6.4,32S17.9,6.4,32,6.4S57.6,17.9,57.6,32S46.1,57.6,32,57.6z" />
      </svg></div>
 function loadYT() {
   const tag = document.createElement("script");
   tag.src = "https://www.youtube.com/iframe_api";
   const firstScriptTag = document.getElementsByTagName("script")[0];
   firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
 }

The code in loadYT is being correctly run on both example pages, causing the iframe code to load after the page is run.

So far as I can tell, lazy load is working properly on both pages. That tells me that you must mean something different instead.

You might need to explain what you want in more detail.

1 Like

I’m trying to implement the lazy load code with the other code.

So that, after the play svg is loaded, them the iframe_api would load after that is clicked.

Lazy Load: I’m trying to achieve this.
https://jsfiddle.net/bs7czfpx/

No Lazy Load
https://jsfiddle.net/182tL0f5/

Ahh okay. The first step is to put the script loading code into a function.

   function loadIframeScript() {
     const tag = document.createElement("script");
     tag.src = "https://www.youtube.com/iframe_api";
     const firstScriptTag = document.getElementsByTagName("script")[0];
     firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
   }

That code was running immediately on running the videoPlayer code. We want to delay that, so we can use an init method.

 const videoPlayer = (function makeVideoPlayer() {
   ...
   function init() {
     loadIframeScript();
   }
   
   return {
     addPlayer,
     play,
     init
   };
 }());

We can now use videoPlayer.init() at any time to load the script, and get things going.

 (function iife() {
   "use strict";

   function coverClickHandler(evt) {
     videoPlayer.init();
     videoPlayer.play(); // problem
   }

But there’s a problem. Because init takes time to do its thing, that play cannot work. We need a way to inform videoPlayer about what we want to do after things are inited. Even though the code has an onPlayerReady function, it’s really bad to make changes to that function, as it causes your code to be extremely brittle and easy to break when you use it in other places.

Instead, we can add a different onPlayerReady method to the videoPlayer code. That way we can check if anything is there and run that, which helps to ensure that nothing else breaks.

Here is that play being put into an onPlayerReady method:

   function coverClickHandler(evt) {
     videoPlayer.init();
     videoPlayer.onReady = function playVideo(videoPlayer) {
       videoPlayer.play();
     };
   }

We just now need the videoPlayer code to check if anything is there, and run it.

   function onPlayerReady(event) {
     ...
     if (videoPlayer.onReady) {
       videoPlayer.onReady(videoPlayer);
     }
   }

Having the code check that onReady exists is a good safety procedure to ensure that the code still runs correctly when it doesn’t exist either.

That is the more appropriate way to do things.

And after a few tweaks to keep JSLint happy, we have https://jsfiddle.net/963jh4wv/1/

1 Like

Compare this: Updated code

https://jsfiddle.net/y9psd1ux/

https://jsitor.com/eqsfmMOx7

With the before code.

https://jsfiddle.net/bs7czfpx/

https://jsitor.com/dzqOZBTW2f

I don’t think the updated code can be adjusted so it looks like the before code.

What do you like about the before code?

With the updated code it flashes when it starts.
https://jsitor.com/eqsfmMOx7

I do not believe that can be fixed.

You shouldn’t be able to see the YouTube playbutton after it opens.

Removing this works.

  /* function play() {
     player.playVideo();
   }*/

/* play*/

/*videoPlayer.play();*/

This can be used instead.
autoplay: 1,

https://jsfiddle.net/amonry72/

https://jsitor.com/rUEB3gbjIk

With the code how it was, it doesn’t flash at all.

https://jsitor.com/dzqOZBTW2f

No youtube play button visible.

Let’s try and give details about what occurs, and the differences of behaviour that you want.

  • With https://jsitor.com/dzqOZBTW2f/ clicking on the cover results in the cover sliding to the sides and the video starts while the slide occurs.
  • With https://jsfiddle.net/bs7czfpx/ clicking on the cover results in things going white, then black, then playing the video.
  • With https://jsfiddle.net/y9psd1ux/ clicking on the cover causes the cover to slide to the sides. While it slides, a background of the cover is also visible, until the youtube video loads and plays.
  • With https://jsitor.com/eqsfmMOx7/ clicking on the cover causes the cover to slide to the sides. While it slides, the same background is visible until the youtube video loads and plays.

That’s happening on those last two because the Youtube Iframe API needs to load, and the youtube video needs to load. That takes time.

You instructed that the Youtube Iframe API does not happen until after someone clicks on the cover. Your instruction is what caused that slowdown.

To not have that slowdown, the Youtube Iframe API needs to be loaded before anyone clicks on the cover. Usually that is done while the page loads.

Loading scripts takes time. A significant delay is unavoidable there.

Question: Why did you want to wait until after someone clicks on the cover, to load the Youtube Iframe API?

2 Likes

If I set up the code doing it this way instead, is the code fine the way it is, or can it be improved further?

Would you leave the iframe-api under the function coverClickHandler(evt), or would you put it into a new function?

    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

https://jsfiddle.net/L4xtwy1r/

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    const curtain = openCurtain(cover);
    showVideo(curtain);
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  }

  const cover = document.querySelector(".play");
  cover.addEventListener("click", coverClickHandler);
}());


const videoPlayer = (function makeVideoPlayer() {
  "use strict";

  let player = null;

  function onPlayerReady(event) {
    player = event.target;
    player.setVolume(100); // percent
  }
  let hasShuffled = false;

There’s so much wrong there.

Just for starters, that click handler function is trying to show a video before the iframe script has even begun to load the script that will eventually create the place for the video.

Why are you delaying the loading of the iframe API until after the click has occurred? That causes so many problems that cannot be fixed.

When I do that, the iframe_api doesn’t appear in network.

I thought it would be another way of doing it.

The iframe_api would only be loaded after clicking the play svg.

What if I moved it outside of the function, something like this.
https://jsfiddle.net/ju91vw23/

Maybe that wouldn’t be good either.

(function manageCurtain() {
  "use strict";

  function show(el) {
    el.classList.remove("hide");
  }


  function hide(el) {
    el.classList.add("hide");
  }

  function loadIframeScript(somethinghere) {
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  }

  function openCurtain(cover) {
    hide(cover);
    const curtain = document.querySelector(".curtain");
    curtain.classList.add("slide");
    return curtain;
  }

  function showVideo(curtain) {
    const thewrap = curtain.parentElement.querySelector(".wrap");
    show(thewrap);
  }

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    const curtain = openCurtain(cover);
    showVideo(curtain);
  }

  const cover = document.querySelector(".play");
  cover.addEventListener("click", coverClickHandler);
}());

I’m confused about what you want. Do you want the iframe_api to not appear in network?

Correct. Yes.

Why do you want the iframe_api to not appear in the network?

Only after the play svg is clicked, then the iframe_api should be loaded.

Less things to load on the page on first load.

Are you aware that loading iframe_api after the click results in a worse experience for the user of the page? It takes time (sometimes a few seconds) to load the script.

1 Like

I have noticed that, and that is fine. As, that is just another way to write the code, depending on how you want it to work.