Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Uncaught TypeError: YT.Player is not a constructor

I hope someone can help with this. I am getting the following error Uncaught TypeError: YT.Player is not a constructor when I am on one tab of about 5 on my page. I click on a button on the page, and it brings up a modal window from which I make a selection and upon the closing of the modal window in the console that error is shown.

What is frustrating is that using the same data with the same compile arguments as production, I cannot get this error to replicate itself in our Dev or Staging environments. The one difference is that our production servers sit behind a NetScaler. Could the NetScaler be the issue?

Below is my Video code.

<div class="videocontainer">
        @if (!string.IsNullOrEmpty(video.VideoURL))
        {

            <script type="text/javascript">
                var _gVideoTracked = false;
                var player;
                function onYouTubeIframeAPIReady() {
                    player = new YT.Player('player', {
                        events: { 'onStateChange': onPlayerStateChange }
                    });
                }
                function onPlayerStateChange(event) {
                    switch (event.data) {
                        case 0:
                            break;
                        case 1:
                            if (!_gVideoTracked) {
                                BaGaTrack('Video Played', 'Played')
                            }
                            _gVideoTracked = true;
                            break;
                        case 2:

                    }
                }
                $(document).ready(function () {
                    $.getScript("https://www.youtube.com/iframe_api", function () {
                        player = new YT.Player('player', {
                            events: { 'onStateChange': onPlayerStateChange }
                        });
                    });
                });
            </script>
            <iframe id="player" src="@video.VideoURL/?enablejsapi=1" allowfullscreen class="video"></iframe>
        }

    </div>
like image 882
Bob Nona Avatar asked Aug 28 '18 16:08

Bob Nona


2 Answers

There seems to be an undocumented API, YT.ready.
Not found in their documentation: https://developers.google.com/youtube/iframe_api_reference

In my case, I had to wrap new YT.Player within YT.read() as shown below.

 function setupPlayer() {
    /**
     * THIS FAILS!!!!!
     */
    // player = new YT.Player("player", {
    //   height: "390",
    //   width: "640",
    //   videoId: "M7lc1UVf-VE",
    //   events: {
    //     onReady: onPlayerReady,
    //     onStateChange: onPlayerStateChange
    //   }
    // });

    /**
     * Need to wait until Youtube Player is ready!
     */
    window.YT.ready(function() {
      player = new window.YT.Player("video", {
        height: "390",
        width: "640",
        videoId: "M7lc1UVf-VE",
        events: {
          onReady: onPlayerReady,
          onStateChange: onPlayerStateChange
        }
      });
    });
  }

I found it via this CodeSanbox sample, https://codesandbox.io/s/youtube-iframe-api-tpjwj

It uses jQuery and I reimplemented it using Vanilla JS below.
https://codesandbox.io/s/soanswer52062169-mem83

For completeness, here is the JavaScript code.

function loadVideo() {
  console.info(`loadVideo called`);

  (function loadYoutubeIFrameApiScript() {
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";

    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    tag.onload = setupPlayer;
  })();

  let player = null;

  function setupPlayer() {
    /**
     * THIS FAILS!!!!!
     */
    // player = new YT.Player("player", {
    //   height: "390",
    //   width: "640",
    //   videoId: "M7lc1UVf-VE",
    //   events: {
    //     onReady: onPlayerReady,
    //     onStateChange: onPlayerStateChange
    //   }
    // });

    /**
     * Need to wait until Youtube Player is ready!
     *
     * YT.ready is not documented in https://developers.google.com/youtube/iframe_api_reference
     * but found from https://codesandbox.io/s/youtube-iframe-api-tpjwj
     */
    window.YT.ready(function() {
      player = new window.YT.Player("video", {
        height: "390",
        width: "640",
        videoId: "M7lc1UVf-VE",
        events: {
          onReady: onPlayerReady,
          onStateChange: onPlayerStateChange
        }
      });
    });
  }

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

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

if (document.readyState !== "loading") {
  console.info(`document.readyState ==>`, document.readyState);
  loadVideo();
} else {
  document.addEventListener("DOMContentLoaded", function() {
    console.info(`DOMContentLoaded ==>`, document.readyState);
    loadVideo();
  });
}

like image 95
dance2die Avatar answered Sep 16 '22 18:09

dance2die


Turns out including the API synchronously doesn't matter (using URL https://www.youtube.com/iframe_api) since the code returned loads the API asynchronously anyway. The instructions are misleading (figures), giving the impression that loading using a direct script link would be synchronous, but that is not the case. As such new YT.Player fails simply because the player 'type' is undefined.

And this is the event you need to wait for:

Any web page that uses the IFrame API must also implement the following JavaScript function:

onYouTubeIframeAPIReady – The API will call this function when the page has finished downloading the JavaScript for the player API, which enables you to then use the API on your page. Thus, this function might create the player objects that you want to display when the page loads.

Warning: Some player-specific functions are also missing until a player itself finishes loading (triggers the "ready" event).

like image 26
James Wilkins Avatar answered Sep 18 '22 18:09

James Wilkins