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

2.) About this one:

  let youtube = document.querySelector('.youtube');

  youtube.addEventListener('click', function() {
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);
  });
}());

It originally had this:
I’m not sure if this piece was needed or not.
If this is needs to be, or is useful to have in there,
then you can add it back in.

Thinking now, it might be good to have in there.

 this.innerHTML ='';
    this.appendChild(iframe);

Seen here:

instead of this:
Maybe not instead of this,
maybe it would just need to be configured properly.
this.parentNode.replaceChild(iframe, this);

I think this would get used:
I think this is the correct one.
this.parentNode.replaceChild(iframe, this);

Instead of this one:
this.appendChild(iframe);

I tried setting it up like this:
the iframe part isn’t working yet.

  let player;
  const load = document.querySelector(".jacketc");
  const iframe = document.querySelector(".jacketc");

  function loadPlayer() {
    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 loadIframe() {
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);
  }

  function loadClickHandler() {
    loadPlayer();
    player.playVideo();
  }

  function iframeClickHandler() {
    loadIframe();
    player.playVideo();
  }

  load.addEventListener("click", loadClickHandler);
  iframe.addEventListener("click", iframeClickHandler);

I also don’t know what these yellow messages mean:
image

I just read this:

If you change the function name to MyController, it is then recognized as a constructor function and that error won’t be given. Give that a shot.

That seems to have worked:
But I’m not sure if I should use that.
https://jsfiddle.net/zb6mkug3/735/

  function MyController() {
  }

Let’s run the code through JSLint to find easy ways to make the code better.

This function needs a “use strict” pragma.

The “use strict” part cannot be a comment, it doesn’t work as a comment, and only achieves something when it is a text string.

(function iife() {
   /* "use strict";*/ // This does nothing when it's as a comment.
  "use strict"; // This string improves how the browser handles the code.

Undeclared ‘document’.

Tell JSLint that you expect that the code be run in a browser.

/*jslint browser */
...

Expected ‘;’ and instead saw ‘,’.

Define values as separate statements, to improve your ability to refactor and improve the code.

        // var div, n, v = document.getElementsByClassName("youtube");
        var div;
        var n;
        var v = document.getElementsByClassName("youtube");

Unexpected ‘for’.

For loops tend to make it more difficult to understand the code. In this case v is a nodeList and we are doing something with each v element, so we can use the forEach method instead.

        // for (n = 0; n < v.length; n++) {
        v.forEach(function (video) {
            div = document.createElement("div");
            // div.setAttribute("data-id", v[n].dataset.id);
            div.setAttribute("data-id", video.dataset.id);
            // div.innerHTML = thumb(v[n].dataset.id);
            div.innerHTML = thumb(video.dataset.id);
            div.onclick = iframe;
            // v[n].appendChild(div);
            video.appendChild(div);
        // }
        });

‘thumb’ is out of scope.

Move the thumb code up above. It’s not vital when the code is a function declaration, but it does make a difference when const/let/var are used to declare functions.

    function thumb(id) {
        ...
    }
    document.addEventListener("DOMContentLoaded", function() {
        ...
    });

    // function thumb(id) {
    //     ...
    // }

Redefinition of ‘thumb’ from line 4.

Don’t repeat function names and variable names, as it leads to confusion that’s best fixed up now. It helps if the function has the more expressive name using a verb+noun format, so we can call this createThumbnail instead.

    // function thumb(id) {
    function createThumbnail(id) {
        ...
    }
    ...
            // div.innerHTML = thumb(video.dataset.id);
            div.innerHTML = createThumbnail(video.dataset.id);

Use double quotes, not single quotes.

Using double quotes is best as we’re more likely to use apostrophes in text, resulting in double quotes around them. Also, we should not be using HTML code within JavaScript, so this is a handy way to find such abuses.

I’ll also separate those multiple var statements while we’re at it too.

        // var thumb = '<img src="https://i.imgur.com/AJDZEOX.jpg">',
        var thumb = "<img src='https://i.imgur.com/AJDZEOX.jpg'>";
        //     play = '<div class="play"></div>';
        var play = "<div class='play'></div>";

‘iframe’ is out of scope.

Move that function up above where it’s used too.

    function createThumbnail(id) {
        ...
    }
    function iframe() {
        ...
    }
    document.addEventListener("DOMContentLoaded", function() {
        ...
    });
    // function iframe() {
    //     ...
    // }

Redefinition of ‘iframe’ from line 9.

Give the iframe function a more expressive name.

    // function iframe() {
    function replaceThumbWithIframe() {

Unexpected ‘this’.

The this keyword can become really confusing about what it refers to, so using a more expressive technique becomes really handy.

    // function replaceThumbWithIframe() {
    function replaceThumbWithIframe(evt) {
        const thumb = evt.currentTarget;
        ...
        // iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
        iframe.setAttribute("src", embed.replace("ID", thumb.dataset.id));
        ...
        // this.parentNode.replaceChild(iframe, this);
        thumb.parentNode.replaceChild(iframe, thumb);
    }

Expected one space between ‘function’ and ‘(’.

It’s better to use named functions instead of using anonymous functions

    // document.addEventListener("DOMContentLoaded", function() {
    document.addEventListener("DOMContentLoaded", function init() {

But that’s not going to work because it’s too late for the DOMContentLoaded event. The onload event and even nowrap at the bottom of the body, ensures that the dom content is already loaded.

So we can turn that into just an ordinary init function and run that instead.

    // document.addEventListener("DOMContentLoaded", function init() {
    function init() {
        ...
    // });
    }
    init();

Unused ‘n’.

After our earlier loop improvement the n index variable is no longer used, and can be removed.

        // var n;

Using const instead of var

You were wanting to use const instead of var, which I fully support too, so we can replace all of the var statements with const instead.

Expected ‘=’ and instead saw ‘;’.

A const variable cannot be unassigned.

        const div;
        const v = document.getElementsByClassName("youtube");

        v.forEach(function (video) {
            div = document.createElement("div");

We should move that const div into the forEach function instead.

        // const div;
        const v = document.getElementsByClassName("youtube");

        v.forEach(function (video) {
            const div = document.createElement("div");

We can now copy this updated code from JSLint back to the JSFiddle code.

Uncaught TypeError: v.forEach is not a function

When trying to run the code, we’re told that v.forEach is not a function. That’s because v is undefined.
Why is v undefined?

        const v = document.getElementsByClassName("youtube");

It seems that getElementsByClassName doesn’t give us a nodeList that we can easily iterate over. That’s alright, for we have querySelectorAll instead.

        // const v = document.getElementsByClassName("youtube");
        const v = document.querySelectorAll(".youtube");

Tidying up the thumbnail

I’m noticing that you want to use a specific thumbnail for this particular example:

    function createThumbnail(id) {
        const thumb = "<img src='https://i.imgur.com/AJDZEOX.jpg'>";
        const play = "<div class='play'></div>";
        return thumb.replace("ID", id) + play;
    }

Instead of changing the createThumbnail code, it’s better to pass in that custom imagename instead:

    function createThumbnail(id) {
        // const thumb = "<img src='https://i.imgur.com/ID.jpg'>";
        const play = "<div class='play'></div>";
        return thumb.replace("ID", id) + play;
    }
    ...
            // div.innerHTML = createThumbnail(video.dataset.id);
            div.innerHTML = createThumbnail("AJDZEOX");

And that’s the code there appropriately updated. https://jsfiddle.net/pmw57/zna6h7r9/

2 Likes

Can I make these lines shorter somehow?

  `document.querySelector(".wrapg.border").style.borderColor = color;`

These lines are over 80 characters says jslint:

   document.querySelector(".wrapg .left").style.borderLeftColor = color;
      document.querySelector(".wrapg .right").style.borderRightColor = color;

This is the whole function:

  function changeBorderColor(playerStatus) {
    let color;
    if (playerStatus === 1) {
      color = "red";
    }
    if (color) {
      document.querySelector(".wrapg.border").style.borderColor = color;
      document.querySelector(".wrapg .left").style.borderLeftColor = color;
      document.querySelector(".wrapg .right").style.borderRightColor = color;
    }
  }

I have an example of how you make a line shorter.

This:

  function hideAllButtons(button) {
    button.querySelectorAll(".play, .pause, .initial, .speaker").forEach(hide);
  }

Becomes this:

  function hideAllButtons(button) {
    var buttonSelectors = ".play, .pause, .initial, .speaker";
    button.querySelectorAll(buttonSelectors).forEach(hide);
  }

Would I be breaking these in two somehow?

document.querySelector(".wrapg.border")
.style.borderColor = color;
document.querySelector(".wrapg .left")
.style.borderLeftColor = color;
document.querySelector(".wrapg .right")
.style.borderRightColor = color;

You can assign the .wrapg element to a variable name and improve things that way.

   if (color) {
     const wrapper = document.querySelector(".wrapg");
     wrapper.style.borderColor = color;
     wrapper.querySelector(".left").style.borderLeftColor = color;
     wrapper.querySelector(".right").style.borderRightColor = color;
   }

A further improvement is to the structure of the code.

Instead of initializing an undefined variable, then assigning it, use the condition in the if statement itself to decide whether to set the colors.

  function changeBorderColor(playerStatus) {
    // let color;
    const color = "red";
    if (playerStatus === 1) {
//       color = "red";
//     }
//     if (color) {
      const wrapper = document.querySelector(".wrapg");
      wrapper.style.borderColor = color;
      wrapper.querySelector(".left").style.borderLeftColor = color;
      wrapper.querySelector(".right").style.borderRightColor = color;
    }
  }
1 Like

I had thought only a ‘var’ could become a ‘const,’ I didn’t know ‘let’ can become a ‘const’ too.

How would I be able to further improve this?

Would I be replacing ‘let’ here?


  function changeBorderColor(playerStatus) {
    const wrapper = document.querySelector(".wrapg");
    let color;
    if (playerStatus === 1) {
      color = "playerStatus";
    }
    if (color) {
      wrapper.classList.add(color);
    }
  }

CSS

.playerStatus {
  border-color: green;
}

.playerStatus .lines:before,
.playerStatus .lines:after {
  background: green;
}

One correction I see would be:
“let” should be on top of “const.”

    let color;
    const wrapper = document.querySelector(".wrapg");

From here, would there be any further improvements that can be made?

 function changeBorderColor(playerStatus) {
    let color;
    const wrapper = document.querySelector(".wrapg");
    if (playerStatus === 1) {
        color = "playerStatus";
    }
    if (color) {
        wrapper.classList.add(color);
    }
}

I think I was able to find a better solution that works better than it did before.

Now the lines don’t wait for the video to load before changing colors.
What I did was replace the ‘hide lines code’ with the ‘color code’ and it worked.
After the video is clicked, the lines change color instantly.

@PaulOB was able to help me with the CSS part:

.playerStatus {
  border-color: green;
}

.playerStatus .lines:before,
.playerStatus .lines:after {
  background: green;
}

and this:
wrapper.classList.add("playerStatus");

  let player;
  /*const load = document.querySelector(".jacketc");
  const wrapper = document.querySelector(".wrapg");


 /* function loadPlayer() {
    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 changeColor() {
  wrapper.classList.add("playerStatus"); 
  }

  /*function loadClickHandler() {
    loadPlayer();
    player.playVideo();
  }*/

  function colorClickHandler() {
    changeColor();
    player.playVideo();
  }
  /*load.addEventListener("click", loadClickHandler);*/
  wrapper.addEventListener("click", colorClickHandler);

  function onPlayerStateChange(evt) {
    // possible states values are:
    // -1 (unstarted), 0 (ended), 1 (playing),
    // 2 (paused), 3 (buffering), 5 (video cued).
    const state = evt.data;
    if (state > 0) {
      changeColor();

    }
  }


How would I get this code:

 <div class="video" data-id="M7lc1UVf-VE">

 let player;
  const load = document.querySelector("     ");

  function loadIframe() {
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);
  }

  function iframeClickHandler() {
    loadIframe();
    player.playVideo();
  }

 iframe.addEventListener("click", iframeClickHandler);

or this code:

This one works without the need for an image:

 <div class="video" data-id="M7lc1UVf-VE">

(function iife() {
  "use strict";

  let player = document.querySelector('.video');

  player.addEventListener('click', function() {
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);
  });
}());

To work with this?

Would I be able to do this, can that be done?

With this?

Why? What do you think that does that isn’t already being done?

1 Like

I want to have the option of using this too, if I want to.

    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);

Would there be a way for me to include that in the code also?

Let’s say I just want this to control this portion, and the audio:

  window.onYouTubePlayerAPIReady = function() {
    const video = document.querySelector(".video");
    const videoId = video.getAttribute("data-id");
    new YT.Player(video, {
      width: 606,
      height: 344,
      videoId: videoId,
      events: {
        "onReady": onPlayerReady,
        "onStateChange": onPlayerStateChange
      }
    });
  };
}());

And I might want this to control a different portion.


    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);

Here’s a blank player without anything added to it:

This could be used to test the other code on.

This is with everything added:

<div class="video " data-id="M7lc1UVf-VE"></div>

(function iife() {
  "use strict";

  let player;

  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) {
    player = event.target;
    player.setVolume(50); // percent
  }


  window.onYouTubePlayerAPIReady = function() {
    const video = document.querySelector(".video");
    const videoId = video.getAttribute("data-id");
    new YT.Player(video, {
      width: 606,
      height: 344,
      videoId: videoId,
      events: {
        "onReady": onPlayerReady

      }
    });
  };
}());

How would I also include this in it?

Ways to test to see if it works is to turn autoplay on
autoplay=1"

or

Change the height and width to see if it changes on the player.

    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=0";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);

This is what I tried:

But it needs a test button on it:
But it probably doesn’t because the player is already loaded.

All that’s being tested is controlling the iframe attributes, and the embed video itself.

I also don’t think a clickhandler is needed to do this.
I could be wrong though.

https://jsfiddle.net/byw5szgt/28/


(function iife() {
  "use strict";

  let player;

  const iframe = document.querySelector(".video");


  function loadIframe() {
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=1";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);
  }


  function iframeClickHandler() {
    loadIframe();
    player.playVideo();
  }


  iframe.addEventListener("click", iframeClickHandler);

  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) {
    player = event.target;
    player.setVolume(50); // percent
  }


  window.onYouTubePlayerAPIReady = function() {
    const video = document.querySelector(".video");
    const videoId = video.getAttribute("data-id");
    new YT.Player(video, {
      width: 606,
      height: 344,
      videoId: videoId,
      events: {
        "onReady": onPlayerReady

      }
    });
  };
}());

The videoPlayer code does the same job as what you are wanting to add up above there. There’s no benefit using both on the same page.

Pick one or the other.

This for the iframe and the embed stuff:

     function iframe() {
         const iframe = document.createElement("iframe");
         const embed = "https://www.youtube.com/embed/ID?autoplay=0";
         iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
         iframe.setAttribute("frameborder", "0");
         iframe.setAttribute("width", "606");
         iframe.setAttribute("height", "344");
         this.parentNode.replaceChild(iframe, this);
     }

And this to control the audio:

let player;

  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) {
    player = event.target;
    player.setVolume(50); // percent
  }


  window.onYouTubePlayerAPIReady = function() {
    const video = document.querySelector(".video");
    const videoId = video.getAttribute("data-id");
    new YT.Player(video, {
      videoId: videoId,
      events: {
        "onReady": onPlayerReady

      }
    });
  };
}());

Is it able to be done?

Looking at both examples:
The 2nd code requires two pieces.

This first code would need another piece on top of it in order for it to connect to the html data-id.

How would I connect this one together by itself?

Html
<div class="video " data-id="M7lc1UVf-VE"></div>

Javascript

(function iife() {
  "use strict";
  const iframe = document.querySelector(".video");

  function Iframe() {
    const iframe = document.createElement("iframe");
    const embed = "https://www.youtube.com/embed/ID?autoplay=1";
    iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
    iframe.setAttribute("frameborder", "0");
    iframe.setAttribute("width", "606");
    iframe.setAttribute("height", "344");
    this.parentNode.replaceChild(iframe, this);
  }

}());

This is how the other version is done by itself:

Html
<div class="video " data-id="M7lc1UVf-VE"></div>

Javascript

(function iife() {
  "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);

  window.onYouTubePlayerAPIReady = function() {
    const video = document.querySelector(".video");
    const videoId = video.getAttribute("data-id");
    new YT.Player(video, {
      width: 606,
      height: 344,
      videoId: videoId,
    });
  };
}());

Can’t you just add this to the url, to control the audio?

&autoplay=1&mute=1

I was thinking, if both of them use this, couldn’t this be taken out and made into its own function?

And that would be used to control both.

function controlBox() {
iframe.setAttribute("src", embed.replace("ID", this.dataset.id));
}

oh, wait, youtube uses this:


    const video = document.querySelector(".video");
    const videoId = video.getAttribute("data-id");

I meant to set the volume.

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