What is the purpose of the load function?

This way works.
https://jsfiddle.net/6wLmhu8a/1/

 const videoPlayer = (function makeVideoPlayer() {
  "use strict";
  const players = [];
  let playerVars = {};
  
  const tag = document.createElement('script');
  tag.src = "https://www.youtube.com/player_api";
  const firstScriptTag = document.getElementsByTagName('script')[0];
  firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

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

  let hasShuffled = false;

  function onPlayerStateChange(event) {
    const player = event.target;
    if (!hasShuffled) {
      player.setShuffle(true);
      player.playVideoAt(0);
      hasShuffled = true;
    }
    if (event.data === YT.PlayerState.PLAYING) {
      for (let i = 0; i < players.length; i++) {
        if (players[i] !== event.target) players[i].pauseVideo();
      }
    }

    if (playerVars.loop && event.data === YT.PlayerState.ENDED) {
      player.seekTo(playerVars.start);
    }
  }

  function addVideo(video, settings) {
    playerVars = Object.assign({
      videoId: video.dataset.id,
      host: "https://www.youtube-nocookie.com",
      events: {
        "onReady": onPlayerReady,
        "onStateChange": onPlayerStateChange
      }
    }, settings);
    players.push(new YT.Player(video, playerVars));
  }

  function init(video, settings) {
    YT.ready(function() {
      addVideo(video, settings);
    });

  }
  return {
    init
  };
}());

I got it working here too.
https://jsfiddle.net/3v2jtqkc/

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

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

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

  let hasShuffled = false;

  function onPlayerStateChange(event) {
    const player = event.target;
    const shufflePlaylist = true;

    if (!hasShuffled) {
      player.setShuffle(shufflePlaylist);
      player.playVideoAt(0);
      hasShuffled = true;
    }
  }

  function addVideo(video) {
    const playlist = "0dgNc5S8cLI,mnfmQe8Mv1g,-Xgi_way56U,CHahce95B1g";
    new YT.Player(video, {

      width: 640,
      height: 360,
      host: "https://www.youtube-nocookie.com",
      playerVars: {
        autoplay: 1,
        controls: 1,
        loop: 1,
        rel: 0,
        iv_load_policy: 3,
        cc_load_policy: 0,
        fs: 0,
        disablekb: 1,
        playlist
      },
      events: {
        "onReady": onPlayerReady,
        "onStateChange": onPlayerStateChange
      }
    });
  }

  function init(opts) {
    addVideo(opts.video);
  }
  return {
    init
  };
}());

Now I remember, it prevents the youtube api from being caught in the browser until it has loaded.

So, that is something I want to keep in the code.

1 Like

I got it.
https://jsfiddle.net/bLg7mw2d/1/

cover.init(".jacket-left");
cover.init(".jacket-middle")
cover.init(".jacket-right")
cover.init(".jacketc")
cover.init(".jacketd", {
  show: ".wraph"
});
1 Like

This is the way I originally had .jacketd written.

https://jsfiddle.net/rLvy6xw7/3/

(function manageCover() {
   "use strict";

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

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

   function coverClickHandler(evt) {
      const cover = evt.currentTarget;
      hide(cover);
      const thewrap = cover.parentNode.querySelector(".wraph");    
      show(thewrap);
   }
   const cover = document.querySelector(".jacketd");
   cover.addEventListener("click", coverClickHandler);
}());

Ahh good, the Google docs say that player_api needs time to get things organised before it can be used.

It also says that it’s been updated to use iframe_api now, instead of player_api, but that player_api will continue to be supported for now.

I recommend that when making your updates, that you rename player_api to be iframe_api. That way you can easily tell from whether the code says iframe_api or player_api if it needs to be updated too.

In here?

From this:

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

to this?

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

What’s the difference between the 2?

The code works with either one.

The difference is that back in 2012 Google renamed it to iframe_api
The player_api works for now, but there’s no guarantee that it will continue to work into the future.

1 Like

Also, onYouTubeIframeAPIReady is preferred to be used instead of onYouTubePlayerAPIReady

Both will work for now, but the Iframe one is the more reliable one to use.

1 Like

Can’t function names be called anything?

Change this:
https://jsfiddle.net/p6qLh5nv/

  function addVideo(video) {
    new YT.Player(video, {


  function init(opts) {
    addVideo(opts.video);
  }
  return {
    init
  };
}());

to

This?
https://jsfiddle.net/tveuLsk4/1/

  function onYouTubeIframeAPIReady(video) {
    new YT.Player(video, {


  function init(opts) {
    onYouTubeIframeAPIReady(opts.video);
  }
  return {
    init
  };
}());

Can you clarify what you mean by this?

I shared an example above here.

Is that what you meant?

Yes. If you go to the API reference page at https://developers.google.com/youtube/iframe_api_reference and search for player_api. you will see that it’s been updated to be iframe_api

Also, if you search for playerapi on the API reference page you will see that refer to a requirements section near the top of the page where it says that the API will call onYouTubeIframeAPIReady when the page has finished downloading.

The way that you’ve done it at https://jsfiddle.net/tveuLsk4/1/ is not correct, as if someone is fast, it’s possible to click the cover before the file has finished downloading.

The onYouTubeIframeAPIReady function must be a global function, so that the google API can run that function once the iframe code has been downloaded.

I’ll work through how that impacts the code shortly.

1 Like

How would I change it, what would I change it to?

Doing things more properly according to Google means making several changes.

The good news is that the following code needs to run immediately when the page loads:

  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 is correctly being done at the start of the videoPlayer IIFE (immediately invoked function expression).

The onYouTubeIframeAPIReady function though is not for us to run in the code. That needs to be globally accessible (outside of any other functions) so that the API can automatically run that function when the API is ready.

I’m going to rename what you have there to addPlayer and make that available from the videoPlayer code. I am also setting autoplay to 0, so that it doesn’t start playing as soon as the iframe is created. I’ll add code to play the video afterwards.

  // function onYouTubeIframeAPIReady(video) {
  function addPlayer(video) {
    const playlist = "0dgNc5S8cLI,mnfmQe8Mv1g,-Xgi_way56U,CHahce95B1g";
    new YT.Player(video, {
...
      playerVars: {
        // autoplay: 1,
        autoplay: 0,
...
  // function init(opts) {
  //   onYouTubeIframeAPIReady(opts.video);
  // }
  return {
    addPlayer
  };
}());

and below that, we’ll have a globally accessible function called onYouTubeIframeAPIReady

function onYouTubeIframeAPIReady() {
    const frameContainer = wrapper.querySelector(".video");
    videoPlayer.addPlayer(frameContainer);
}

The wrapper is not yet available. We can’t pass config information to the onYouTubeIframeAPIReady function, so we’ll get that config information from else where.

Where is a good place for config information? At the top of the code of course.

const cover = document.querySelector(".jacket");
...
  // const cover = document.querySelector(".jacket");
  cover.addEventListener("click", coverClickHandler);

We can now refer to that cover from within the onYouTubeIframeAPIReady function too:

function onYouTubeIframeAPIReady() {
    const wrapper = cover.nextElementSibling;
    const frameContainer = wrapper.querySelector(".video");
    videoPlayer.addPlayer(frameContainer);
}

When the player is ready, the onPlayerReady function is run:

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

We want to later on play the video when someone clicks on the cover. It is from that onPlayerReady function that we save aside a reference to the player.

const videoPlayer = (function makeVideoPlayer() {
  "use strict";
  let player = null;
...
  function onPlayerReady(event) {
    // const player = event.target;
    player = event.target;
    player.setVolume(100); // percent
  }

That way, when the cover is clicked, we can have the videoPlayer code play the video.

  function play() {
    player.playVideo();
  }
  return {
    addPlayer,
    play
  };
}());

Lastly, we remove the initPlayer code because the different videoPlayer.play code is being used instead.

  // function initPlayer(wrapper) {
  //   videoPlayer.init({
  //     video: wrapper.querySelector(".video")
  //   });
  // }

  function coverClickHandler(evt) {
    const wrapper = evt.currentTarget.nextElementSibling;
    show(wrapper);
    // initPlayer(wrapper);
    videoPlayer.play();
  }

The way it all works is that the iframe_api is loaded as soon as possible, which when loaded creates the iframe as soon as possible. The video is ready and waiting, but not playing.
Later on when we click the cover, the video starts playing.

The updated code is at https://jsfiddle.net/71y52qa8/

1 Like

What about the codes that use js.load

Those are fine?

https://jsfiddle.net/f26o57kt/2/

const load = (function() {
  "use strict";

  function _load(tag) {
    return function(url) {
      return new Promise(function(resolve) {
        const element = document.createElement(tag);
        const parent = "body";
        const attr = "src";
        element.onload = function() {
          resolve(url);
        };
        element[attr] = url;
        document[parent].appendChild(element);
      });
    };
  }
  return {
    js: _load("script")
  };
}());
(function manageCover() {
  "use strict";

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

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    hide(cover);
  }
  const cover = document.querySelector(".jacket");
  cover.addEventListener("click", coverClickHandler);
}());
const videoPlayer = (function makeVideoPlayer() {
  "use strict";

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

  function onPlayerStateChange(event) {
    const player = event.target;
    const shufflePlaylist = true;

    if (!hasShuffled) {
      player.setShuffle(shufflePlaylist);
      player.playVideoAt(0);
      hasShuffled = true;
    }
  }

  function addVideo(video) {

    const playlist = "0dgNc5S8cLI,mnfmQe8Mv1g,-Xgi_way56U,CHahce95B1g";

    new YT.Player(video, {
      
      width: 640,
      height: 360,
      host: "https://www.youtube-nocookie.com",
      playerVars: {
        autoplay: 1,
        controls: 1,
        loop:1,
        rel: 0,
        iv_load_policy: 3,
        cc_load_policy: 0,
        fs: 0,
        disablekb: 1,
        playlist
      },
      events: {
        "onReady": onPlayerReady,
        "onStateChange": onPlayerStateChange
      }
    });
  }

  function init(opts) {
    load.js("https://www.youtube.com/player_api").then(function() {
      YT.ready(function() {
        addVideo(opts.video);
      });
    });
  }
  return {
    init
  };
}());

The load code is completely unwanted. The iframe_api takes care of that all by itself.

This is needed though.

const load = (function() {
  "use strict";

  function _load(tag) {
    return function(url) {
      return new Promise(function(resolve) {
        const element = document.createElement(tag);
        const parent = "body";
        const attr = "src";
        element.onload = function() {
          resolve(url);
        };
        element[attr] = url;
        document[parent].appendChild(element);
      });
    };
  }
  return {
    js: _load("script")
  };
}());

Why do you think that it is needed? The youtube API takes care of all of that.

It prevents the youtube api from being caught in the browser until it has loaded.

That is also what the onYouTubeIframeAPIReady function achieves.

The load code is a useless complication. It’s better to do things the way that YouTube instructs in their API documentation.