11
votes

I'm using the iframe YouTube API and I want to track events, for example, sending data to google analytics, when user start and stop video.

<iframe src="https://www.youtube.com/embed/DjB1OvEYMhY"></iframe>

I looked https://developers.google.com/youtube/iframe_api_reference?csw=1 and did not find an example how to do that. The example creates iframe and defines onReady and onStateChange as well. How would I do same when I've only iframe on page?

3
You should be able to put in an event handler along the lines of function getPlayerState() {if (ytplayer) {return ytplayer. getPlayerState();}} - hopefully someone can demo how this produces results. Can't really help I'm afraid.redditor

3 Answers

16
votes

This example listens to every play/pause action the user makes, using onPlayerStateChange with its different states, and prints (records) them.

However, you need to create your own record function to do whatever you want with this data.

You also need an ID on your iframe (#player in this case) and to add ?enablejsapi=1 at the end of its URL. And of course, make sure to include the Youtube iframe API.

Note

It's important to declare the API after your code, because it calls onYouTubeIframeAPIReady when it's ready.

<!DOCTYPE html>
<html>
<body>
    <iframe id="player" src="https://www.youtube.com/embed/DjB1OvEYMhY?enablejsapi=1"></iframe>
    <h5>Record of user actions:</h5>
    <script>
      var player;
      function onYouTubeIframeAPIReady() {
        player = new YT.Player( 'player', {
          events: { 'onStateChange': onPlayerStateChange }
        });
      }
      function onPlayerStateChange(event) {
        switch(event.data) {
          case 0:
            record('video ended');
            break;
          case 1:
            record('video playing from '+player.getCurrentTime());
            break;
          case 2:
            record('video paused at '+player.getCurrentTime());
        }
      }
      function record(str){
        var p = document.createElement("p");
        p.appendChild(document.createTextNode(str));
        document.body.appendChild(p);
      }
    </script>
    <script src="https://www.youtube.com/iframe_api"></script>
</body>
</html>

JS Fiddle Demo

3
votes

Here is a version that doesn't use Youtubes iframe API script. The only drawback is that the iframe API might change.

<iframe id="player" src="https://www.youtube.com/embed/dQw4w9WgXcQ?enablejsapi=1"></iframe>
var addYoutubeEventListener = (function() {

    var callbacks = [];
    var iframeId = 0;

    return function (iframe, callback) {

        // init message listener that will receive messages from youtube iframes
        if(iframeId === 0) {
            window.addEventListener("message", function (e) {

                if(e.origin !== "https://www.youtube.com" || e.data === undefined) return;
                try {
                    var data = JSON.parse(e.data);
                    if(data.event !== 'onStateChange') return;

                    var callback = callbacks[data.id];
                    callback(data);
                }
                catch(e) {}
            });
        }

        // store callback
        iframeId++;
        callbacks[iframeId] = callback;
        var currentFrameId = iframeId;

        // sendMessage to frame to start receiving messages
        iframe.addEventListener("load", function () {
            var message = JSON.stringify({
                event: 'listening',
                id: currentFrameId,
                channel: 'widget'
            });
            iframe.contentWindow.postMessage(message, 'https://www.youtube.com');

            message = JSON.stringify({
                event: "command",
                func: "addEventListener",
                args: ["onStateChange"],
                id: currentFrameId,
                channel: "widget"
            });
            iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
        });
    }
})();
addYoutubeEventListener(document.getElementById("player"), function(e) {

    switch(e.info) {
        case 1:
            // playing
            break;
        case 0:
            // ended
            break;
    }
});
0
votes

Sometimes the event load is not enough to ensure that the document inside the iframe is ready. If the iframe is in a different domain it is not possible to subscribe to see when it is ready.

A possible workaround is to record when an event is received from the iframe, if after subscribing no event was received try again:

var addYoutubeEventListener = (function() {

   var callbacks = [];
   var iframeId = 0;
   var subscribed = [];

   return function (iframe, callback) {
       // init message listener that will receive messages from youtube iframes
       if(iframeId === 0) {
           window.addEventListener("message", function (e) {
               if(e.origin !== "https://www.youtube.com" || e.data === undefined) return;
               try {
                   var data = JSON.parse(e.data);
                   subscribed[data.id] = true;
                   if(data.event !== 'onStateChange') return;

                   var callback = callbacks[data.id];
                   callback(data);
               }
               catch(e) {}
           }, true);
       }

       // store callback
       iframeId++;
       callbacks[iframeId] = callback;
       subscribed[iframeId] = false;
       var currentFrameId = iframeId;

       //console.log("adding event listener to iframe id " + iframeId);

       // sendMessage to frame to start receiving messages
       iframe.addEventListener("load", function () {
           var tries = 0;
           var checkSubscribed = function()
           {
               if (subscribed[currentFrameId]) {
                   //console.log("subscribed succesfully " + currentFrameId)
               }
               else
               {
                   tries++;
                   //console.log("Try again " + currentFrameId + " (" + tries + ")");
                   if (tries < 100) {
                       doSubscribe();
                   }
                   else
                   {
                       console.log("Unable to subscribe" + currentFrameId );
                   }
               }
           }
           var doSubscribe = function()
           {
               var message = JSON.stringify({
                    event: 'listening',
                    id: currentFrameId,
                    channel: 'widget'
                });
                iframe.contentWindow.postMessage(message, 'https://www.youtube.com');

                message = JSON.stringify({
                    event: "command",
                    func: "addEventListener",
                    args: ["onStateChange"],
                    id: currentFrameId,
                    channel: "widget"
                });
                iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
                setTimeout(checkSubscribed, 100);
            };
            doSubscribe();
       }, true);
   }
})();