Ajit Kumar
Ajit Kumar

Reputation: 417

'YT' is not defined - Youtube Player API

I am using Youtube Player API on my React site. When I try it on the site, it gives this error: TypeError: Cannot read property 'ready' of undefined. Here is the code I am using:

var player;
function loadVideo() {
    window.YT.ready(function () {
      new window.YT.Player("player", {
        height: "390",
        width: "640",
        videoId: "M7lc1UVf-VE",
        events: {
          onReady: onPlayerReady,
          onStateChange: onPlayerStateChange,
        },
      });
    });

    function onPlayerReady(event) {
      event.target.playVideo();
      player = event.target;
    }

    function onPlayerStateChange(event) {
      var videoStatuses = Object.entries(window.YT.PlayerState);
      console.log(videoStatuses.find((status) => status[1] === event.data)[0]);
    }
  }

  useEffect(() => {
    setMaxDuration("06:00");
    var tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    loadVideo();
  }, []);

Iframe:

  <div id="player">
    <iframe
      title="p"
      id="player"
      width="560"
      height="315"
      src="https://www.youtube.com/embed/sGPrx9bjgC8&autoplay=1"
      frameBorder="0"
      allowFullScreen
    ></iframe>
  </div>

(edited the code to the latest workaround) How can I get this to work?

Upvotes: 0

Views: 4727

Answers (2)

Guerric P
Guerric P

Reputation: 31815

You have to load the library as suggested by the documentation. The code below loads the library asynchronously, then the library "calls back" your code when ready via the global onYouTubeIframeAPIReady callback (the name is mandatory).

var tag = document.createElement('script');

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

function onYouTubeIframeAPIReady() {
  console.log('Yay, YT exists!', YT);
}

When doing a bit of reverse engineering in the YouTube library code we can see this line (unminified by myself):

const callback = window.onYouTubeIframeAPIReady;
callback && callback();

Which shows how this function is being called and why it doesn't throw any error if it doesn't exist.


As I read your question again, you're using React, so the previous solution might not integrate very well, you might prefer not to use the global callback and use this instead:

const useYoutube = callback => {
  useEffect(() => {
    if (!window.YT) {
      var tag = document.createElement('script');
      tag.src = 'https://www.youtube.com/iframe_api';
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
      tag.onload = callback;
    } else {
      callback();
    }
  }, []);
};

This custom hook will load the YouTube library only once, lazily (the first time it is needed). You could import it in every component that needs it, and use it with useYoutube(loadVideo).

Here is a working Stackblitz demo

Works also in local: enter image description here

Upvotes: 5

David Bradshaw
David Bradshaw

Reputation: 13077

The reason the the object shows up in the console, is that the console runs asynchronously from you code. When you have the error YT is not yet loaded, but it has been by the time the console is displayed.

You can prove this be changing your console log to parse the object into a string with JSON.serialise() which will then log the runtime state of the object.

Upvotes: 0

Related Questions