"Tracking doesn’t have to come at the cost of privacy"

What is the Problem?
Google Tag Manager (GTM) provides built-in ways to track YouTube videos, but things get tricky when you’re using privacy-friendly youtube-nocookie.com. This method enhances privacy by preventing cookies, but it also removes the traditional tracking abilities provided by YouTube's standard embed code.

So how can you still track important metrics like play, pause, and complete events without violating privacy regulations?


Solution:
Using GTM and the YouTube Iframe API, you can monitor key video events without violating privacy policies. Here's my solution:

  1. We would need to define a variable for tracking one of the youtube elements. Let's choose video label, which will be pushed to the Data Layer when activated.

  2. We need a trigger for capturing youtube event from our future listener. Let's name it YT Event with youtube event name. This trigger will link to our GA4 Tag with the same event name and our variable as a parameter that we created in our first step.

  3. Now, we go to the most intriguing and difficult part: the listener. There are some internet sources with their own versions of listener, however if your Youtube videos src container appears only after clicking on it and not on the load of the page, you would have to think about your own listener. Worry not! Here is the solution: We need to set up a listener tag, which will always try to capture youtube presence on the page. The way to do it is to trigger it on every click on your website (or you can come up with your own more effective way) with the following JS code:

<script>

        setInterval(function() {

//enable the JavaScript API for an embedded player

        for (var e = document.getElementsByTagName("iframe"), x = e.length; x--;)

                if (/youtube-nocookie.com\/embed/.test(e[x].src))

                        if (e[x].src.indexOf('enablejsapi=') === -1)

                                e[x].src += (e[x].src.indexOf('?') === -1 ? '?' : '&') + 'enablejsapi=1';

        },1000)

        var gtmYTListeners = [],

        gtmYTListenersStates = []; // support multiple players on the same page

// attach our YT listener once the API is loaded

function onYouTubeIframeAPIReady()

{

  console.log(1);

        for (var e = document.getElementsByTagName("iframe"), x = e.length; x--;)

        {

                if (/youtube-nocookie.com\/embed/.test(e[x].src))

                {

                        gtmYTListeners.push(new YT.Player(e[x],

                        {

                                events:

                                {

                                        onError: onPlayerError,

                                        onReady: onPlayerReady,

                                        onStateChange: onPlayerStateChange

                                }

                         }));

                         YT.gtmLastAction = "p";

                }

        }

}

function onPlayerReady(e)

{

         var url = e.target.getVideoUrl();

         gtmYTListenersStates[url] = e.target.getPlayerState();

         setInterval(function ()

         {

                  var state = e.target.getPlayerState();

                  if (gtmYTListenersStates[url] !== state)

                  {

                          e.data = state;

                          onPlayerStateChange(e);

                   }

                   gtmYTListenersStates[url] = state;

           }, 100);

}

// listen for play, pause and end states

// also report % played every second

function onPlayerStateChange(e)

{

         e["data"] == YT.PlayerState.PLAYING && setTimeout(onPlayerPercent, 1000, e["target"]);

         var video_data = e.target["getVideoData"](),

                 label = video_data.title;

         // Get title of the current page

         var pageTitle = document.title;

         if (e["data"] == YT.PlayerState.PLAYING && YT.gtmLastAction == "p")

         {

                label = "Video Played - " + video_data.title;

                dataLayer.push(

                {

                         'event': 'youtube',

                         'eventCategory': 'Youtube Videos',

                         'eventAction': pageTitle,

                         'eventLabel': label

                 });

                 YT.gtmLastAction = "";

           }

           if (e["data"] == YT.PlayerState.PAUSED)

           {

                    label = "Video Paused - " + video_data.title;

                    dataLayer.push(

                    {

                             'event': 'youtube',

                             'eventCategory': 'Youtube Videos',

                             'eventAction': pageTitle,

                             'eventLabel': label

                     });

                     YT.gtmLastAction = "p";

              }

}

// catch all to report errors through the GTM data layer

// once the error is exposed to GTM, it can be tracked in UA as an event!

function onPlayerError(e)

{

           dataLayer.push(

           {

                   'event': 'error',

                   'eventCategory': 'Youtube Videos',

                   'eventAction': 'GTM',

                   'eventLabel': "youtube:" + e["target"]["src"] + "-" + e["data"]

            })

}

// report the % played if it matches 0%, 25%, 50%, 75% or completed

function onPlayerPercent(e)

{

          console.log("onPlayerPercent");

          if (e["getPlayerState"]() == YT.PlayerState.PLAYING)

          {

                   var t = e["getDuration"]() - e["getCurrentTime"]() <= 1.5 ? 1 : (Math.floor(e["getCurrentTime"]() / e

                   ["getDuration"]() * 4) / 4).toFixed(2);

                   if (!e["lastP"] || t > e["lastP"])  

                   {

                            var video_data = e["getVideoData"](),

                                     label = video_data.title;

                            // Get title of the current page

                            var pageTitle = document.title;

                            e["lastP"] = t;

                            label = t * 100 + "% Video played - " + video_data.title;

                            dataLayer.push(

                            {

                                     'event': 'youtube',

                                     'eventCategory': 'Youtube Videos',

                                     'eventAction': pageTitle,

                                     'eventLabel': label

                            })

                     }

                     e["lastP"] != 1 && setTimeout(onPlayerPercent, 1000, e);

            }

}

// load the Youtube JS api and get going

var j = document.createElement("script"),

        f = document.getElementsByTagName("script")[0];

j.src = "//www.youtube.com/iframe_api";

j.async = true;

f.parentNode.insertBefore(j, f); 

</script>

How It Works:

  • API Activation: The YouTube Iframe API is loaded and constantly monitors for YouTube iframes.

  • Event Tracking: The custom listener tracks key video interactions (play, pause, and percentage watched) and pushes them to the Data Layer.

  • Triggering Tags: GTM uses these Data Layer pushes to trigger tags for each video event.

With this setup, you can track YouTube interactions while complying with privacy regulations, as no cookies are involved.


And there you have it! With this setup, you can keep an eye on how users interact with your YouTube videos while still playing by the privacy rules. No cookies, no worries—just the data you need. I spent a fair bit of time researching and tweaking outdated methods to come up with this solution, so I hope it saves you some headaches.

Reference:

As I mentioned, here are some of the sources I used for trying to implement it and ended up enhancing some outdated methods :)

©2024 Data Stables· All rights reserved.

©2024 Data Stables· All rights reserved.

©2024 Data Stables· All rights reserved.