Preventing the browser from reading the YouTube code until the image was clicked

We can start from the outside then, from the following code:

    videoPlayer.init(wrapper.querySelector(".video"));

When there are multiple setting being given to an init function, it’s preferable to give them as different object properties.

So to start, we move the wrapper information to an object property, which needs to be done to each call to the init function.

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

We can then refer to that video property from the init function.

  // function init(video) {
  function init(opts) {
    loadPlayer();
    timer = setInterval(function checkAPIReady() {
      if (apiIsReady) {
        timer = clearInterval(timer);
        // addVideo(video);
        addVideo(opts.video);
      }
    }, 100);
  }

The reason why this is preferable is because it’s much better to use an object to contain multiple settings, than to give multiple parameters to a function. When reasons occur for more values to be given, adding another function parameter is much more disruptive than adding another object property instead.

We can now add that start and end information to a playerVars property when initializing the video player.

    videoPlayer.init({
        video: wrapper.querySelector(".video"),
        playerVars: {
            start: 30,
            end: 50
        }
    });

and we can pass that on in the init function

As it’s best for a module such as the videoPlayer function to have a reliable and consistent interface, that’s why it’s best to give settings as option properties.

When inside of the module, it’s less disruptive for us to change function parameters when needed, but more than three function parameters does need us to carefully consider things.

Fortunately it’s only one function parameter being extended to two with this change that’s going to happen here.

    // function addVideo(video) {
    function addVideo(video, playerVars) {
        ...
    }
        // addVideo(opts.video);
        addVideo(opts.video, opts.playerVars);

We have now reduced the problem to a smaller and simpler one. That being, how do we add a playerVars object to the existing playerVars?

I’ll cover that in the next post.

1 Like

The solution to the problem is to loop through each of the playerVars properties and add them to the defaultPlayerVars.

We can make a start on this by creating a defaultPlayerVars variable.

    const defaultPlayerVars = {
        autoplay: 0,
        controls: 1,
        showinfo: 1,
        rel: 0,
        iv_load_policy: 3,
        cc_load_policy: 0,
        fs: 0,
        disablekb: 1
    };
    new YT.Player(video, {
      width: 200,
      height: 200,
      videoId: videoId,
      // const defaultPlayerVars = {
      //   autoplay: 0,
      //   controls: 1,
      //   showinfo: 1,
      //   rel: 0,
      //   iv_load_policy: 3,
      //   cc_load_policy: 0,
      //   fs: 0,
      //   disablekb: 1
      // },
      events: {
        "onReady": onPlayerReady
      }
    });

Each of these changes being made help us to gradually get closer to our desired objective, while also keeping the code in a fully working state to help us ensure that we haven’t broken things.

We can now get each key from the playerVars object, and use reduce to combine them with the default settings:

    const combinedPlayerVars = Object.keys(playerVars).reduce(function (obj, key) {
        obj[key] = playerVars[key];
        return obj;
    }, defaultPlayerVars);
    new YT.Player(video, {
      width: 200,
      height: 200,
      videoId: videoId,
      // defaultPlayerVars,
      combinedPlayerVars,
      events: {
        "onReady": onPlayerReady
      }
    });

And we can now successfully add playerVars values to the video player.

The last thing to do is to update the init function so that playerVars behaves properly even when they’re not given.

    function init(opts) {
      ...
                // addVideo(opts.video, opts.playerVars);
                addVideo(
                    opts.video, 
                    opts.playerVars || {}
                );
        ...
    }

And now, when you add playerVars to any of the videoPlayer.init lines of code it will be properly understood.

1 Like

Thank you. I have another question.

How would I have gotten the 2nd box to open here?

When it’s clicked on, it won’t open.

What would’ve worked?

How come only .playa opens

.playb doesn’t open at all.

That’s something I could not figure out why.

There can only ever be one YouTube API. Multiple ones are not allowed.

1 Like

Really, how come?

Now I know why it wouldn’t work.

It’s a good thing that you have what I was doing above, which does work instead.

1 Like

When the video class is underneath it’s function

  window.onYouTubePlayerAPIReady = function() {
    const video = document.querySelector(".video");

Then this can be written like this:
videoPlayer.init();

When it’s not there:

  function addVideo(video) {
    const videoId = video.getAttribute("data-id");
    new YT.Player(video, {

Then it gets written like this:
videoPlayer.init(wrapper.querySelector(".video"));

Am I right about this?

That first one is has more primitive videoplayer code that doesn’t let you specify the wrapper element that contains the video.

The second one lets you specify the wrapper element.

I think what you’re saying is:

This isn’t meant to work on the 1st code:
(wrapper.querySelector(".video"));

Only the 2nd code.

1 Like

Yes, the first set of code would just ignore that instead.

1 Like

Oh yes, and of course, there is an easier way to do the following:

    const combinedPlayerVars = Object.keys(playerVars).reduce(function (obj, key) {
        obj[key] = playerVars[key];
        return obj;
    }, defaultPlayerVars);

Oh and of course, we can use Object.assign() to achieve what the above does in a much simpler manner:

    const combinedPlayerVars = Object.assign(defaultPlayerVars, playerVars);
1 Like

Before adding initButton:

After adding initButton:
How would I have gotten this one to work using the init button?


Update:

I forgot to add this:

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

Now it works:
https://jsfiddle.net/pezuLqvo/56/


(function manageCover() {
  "use strict";
  const hide = (el) => el.classList.add("hide");

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

  function loadPlayer() {
    const iframeContainer = document.querySelector(".video");

    function loadIframe(iframeContainer) {
      const iframe = document.createElement("iframe");
      const embed = "https://www.youtube-nocookie.com/embed/ID?autoplay=1";
      iframe.setAttribute("src", embed.replace("ID", iframeContainer.dataset.id));
      iframe.setAttribute("frameborder", "0");
      iframe.setAttribute("width", "600");
      iframe.setAttribute("height", "338");
      iframe.setAttribute("allow", "autoplay; encrypted-media");
      iframeContainer.parentNode.replaceChild(iframe, iframeContainer);
    }
    loadIframe(iframeContainer);

  }

  function init() {
    loadPlayer();
  }
  return {
    init
  };

}());


(function iife() {
  "use strict";
  const show = (el) => el.classList.remove("hide");

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

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

What’s the reason why multiples of these won’t work?
This one doesn’t use any api’s.

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

  function loadPlayer() {
    const iframeContainer = document.querySelector(".video");

    function loadIframe(iframeContainer) {
      const iframe = document.createElement("iframe");
      const embed = "https://www.youtube-nocookie.com/embed/ID?autoplay=1";
      iframe.setAttribute("src", embed.replace("ID", iframeContainer.dataset.id));
      iframe.setAttribute("frameborder", "0");
      iframe.setAttribute("width", "600");
      iframe.setAttribute("height", "338");
      iframe.setAttribute("allow", "autoplay; encrypted-media");
      iframeContainer.parentNode.replaceChild(iframe, iframeContainer);
    }
    loadIframe(iframeContainer);

  }

  function init() {
    loadPlayer();
  }
  return {
    init
  };

}());


(function iife() {
  "use strict";
  const show = (el) => el.classList.remove("hide");

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

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


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

  function loadPlayer() {
    const iframeContainer = document.querySelector(".video");

    function loadIframe(iframeContainer) {
      const iframe = document.createElement("iframe");
      const embed = "https://www.youtube-nocookie.com/embed/ID?autoplay=1";
      iframe.setAttribute("src", embed.replace("ID", iframeContainer.dataset.id));
      iframe.setAttribute("frameborder", "0");
      iframe.setAttribute("width", "600");
      iframe.setAttribute("height", "338");
      iframe.setAttribute("allow", "autoplay; encrypted-media");
      iframeContainer.parentNode.replaceChild(iframe, iframeContainer);
    }
    loadIframe(iframeContainer);

  }

  function init() {
    loadPlayer();
  }
  return {
    init
  };

}());


(function iife() {
  "use strict";
  const show = (el) => el.classList.remove("hide");

  function coverClickHandler(evt) {
    const wrapper = evt.currentTarget.nextElementSibling;
    show(wrapper);
    videoPlayerb.init();
  }

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

That’s because each of them is using the same container.

Now they are using different containers:

Still not working:

Or was I supposed to change something else?

How would they each use a different container?


function loadPlayer() {
    const iframeCona = document.querySelector(".video");

    function loadIframe(iframeCona) {
      const iframe = document.createElement("iframe");
      const embed = "https://www.youtube-nocookie.com/embed/ID?autoplay=1";
      iframe.setAttribute("src", embed.replace("ID", iframeCona.dataset.id));
      iframe.setAttribute("frameborder", "0");
      iframe.setAttribute("width", "600");
      iframe.setAttribute("height", "338");
      iframe.setAttribute("allow", "autoplay; encrypted-media");
      iframeCona.parentNode.replaceChild(iframe, iframeCona);
    }
    loadIframe(iframeCona);

  }





  function loadPlayer() {
    const iframeConb = document.querySelector(".video");

    function loadIframe(iframeConb) {
      const iframe = document.createElement("iframe");
      const embed = "https://www.youtube-nocookie.com/embed/ID?autoplay=1";
      iframe.setAttribute("src", embed.replace("ID", iframeConb.dataset.id));
      iframe.setAttribute("frameborder", "0");
      iframe.setAttribute("width", "600");
      iframe.setAttribute("height", "338");
      iframe.setAttribute("allow", "autoplay; encrypted-media");
      iframeConb.parentNode.replaceChild(iframe, iframeConb);
    }
    loadIframe(iframeConb);

  }

I am getting some surgery soon, but needless to say is that doubling up on things is not a beneficial technique.

I just wanted to know how they would each use a different container.
Because you said that the reason why it doesn’t work is because they both use the same container.

I never said I was using this, I just wanted to know how it would’ve been accomplished.
It’s good to know how to do these things.

I know it’s not beneficial, I just wanted to know how I would solve the container issue, unless the container issue would be too much of a hassle to even try to fix.

I was only able to get this far, then it all became confusing.

  function addVideo(video) {
    const videoId = video.getAttribute("data-id");
        const defaultPlayerVars = {
        autoplay: 0,
        controls: 1,
        showinfo: 1,
        rel: 0,
        iv_load_policy: 3,
        cc_load_policy: 0,
        fs: 0,
        disablekb: 1
    };
    new YT.Player(video, {
      width: 200,
      height: 200,
      videoId: videoId,
      // const defaultPlayerVars = {
      //   autoplay: 0,
      //   controls: 1,
      //   showinfo: 1,
      //   rel: 0,
      //   iv_load_policy: 3,
      //   cc_load_policy: 0,
      //   fs: 0,
      //   disablekb: 1
      // },
      events: {
        "onReady": onPlayerReady
      }
    });
  }
  let apiIsReady = false;
  let timer;
  window.onYouTubePlayerAPIReady = function() {
    apiIsReady = true;
  }

  // function init(video) {
  function init(opts) {
    loadPlayer();
    timer = setInterval(function checkAPIReady() {
      if (apiIsReady) {
        timer = clearInterval(timer);
        // addVideo(video);
        addVideo(opts.video);
      }
    }, 100);
  }
  
      videoPlayer.init({
        video: wrapper.querySelector(".video"),
        playerVars: {
            start: 30,
            end: 50
        }
    });

How you would solve the container issue is to use one videoPlayer set of code, and pass a different container in to the init function. That init function can then give the container to the loadPlayer function.

The most recommended way of giving that information to the init function is using an opts object, as that lets you easily expand it to allow other options on an as-needed basis.

The worse alternative is to change the init function parameters by adding or changing parameters, which forces you to then rewrite everything else that calls the init function. That’s not preferable, thus the preferred method of using an opts object instead.

That is how you would solve the issue.

1 Like