Tracking YouTube Videos in iFrames with Adobe Launch: A Simpler Approach

Making this process a little less painful by going as directly as possible to the YouTube API.

Introduction

Embedded YouTube videos in iFrames are everyone’s favorite (read: least favorite) scenario to encounter when designing tracking for a site, particularly if you do not have access to development resources to send details on video activity. Fortunately, there are ways for you to manage this entirely yourself!

What Doesn’t Work

First, let’s rule out some things that won’t work in this scenario so you know what to avoid:

The Built-in Core Extension’s Media – Play, Pause, etc. Events: Using the CSS selector of the iFrame or the surrounding components will not properly pick up the events in this scenario.

The Adobe Analytics for Video Launch Extension: This still requires you to take an additional step of mapping the events received from YouTube, which we’ll do here with some simple code without having to use this extension.

The YouTube Embed Extension for Adobe Launch: If your video already exists on the page, this won’t help you, as this extension is used to insert videos on the page.

Past Solutions from DTM

Next, let’s discuss some past solutions from the world of DTM:

Adobe’s DTM Recommendation: This requires additional parameters to be added onto the video URL (which we can do from our side in the tag manager, rather than rely on making updates across the whole site). It also requires the additional of a large custom code block for the Adobe Media Tracking code.

33Sticks DTM Recommendation: This is a great place to start, as it automatically appends the proper parameters onto the video with no work from other developers. However, perhaps you’re trying to reduce the amount of custom code you have overall, and you may not want to rely on the continued functioning of the raw Adobe Analytics Media Module code in the world of Launch.

Can we do the same thing with fewer lines of code and without relying on things like Heartbeat, the Media Module, etc.? Turns out we can!

How This All Got Started

I was working through a particularly ornery YouTube video tracking scenario and wanted to start completely from scratch to get the cleanest possible solution with the least amount of code that I felt would maintain well long-term. The YouTube API seems fairly stable, and as long as that doesn’t change significantly (and if it did, it would be clearly announced), this code should continue to work for a good while.

I was playing around with multiple options, and had a jam session with the fantastic Jim Gordon, owner of what I’ll call my “brother site”, Jimalytics and creator of the Tagtician Chrome extension. We both played around in the console for awhile, I hit upon the different event states, and we had an impromptu hackathon where this all came together pretty quickly. The majority of the second code block is based his work while we talked through it in that session. I’ve made additional modifications for my use case and written up the details so we can share this with the world.

Requirements for this Solution

We’ll need the following items for this work:

  • The addition of the enablejsapi parameter to the video URL and a unique player ID to be appended to each video on the page (even if you have only one video on the page, it still needs an ID). This allows the items from within the iFrame to communicate with the surrounding site.
  • Code to listen for the YouTube events and map them to _satellite.track call
  • Rules set up in Adobe Launch to listen for the direct calls you set up above
  • Events and Variables configured in Adobe Analytics admin to receive these events

Assumptions: you are running an existing, functional implementation of Adobe Analytics and Experience Cloud ID service on your site.

Part 1: Adding the enablejsapi parameter and unique player IDs to all YouTube videos on the site

Jason Thompson has already done exactly this in the first part of the above 33Sticks post, so we can use the same method – no need to change what works!

We’ll use this to append the parameters and IDs in cases where we see that the Enable JS API parameter is not already present.

This code should go in the Adobe Analytics extension in the “Configure Tracker Using Custom Code” section. (General note: when copying/pasting code, ensure proper formatting is retained, and ideally paste into a plaintext file or code editor to save locally before adding to Adobe Launch.)

/*Video Code Block 1 of 2: Append Enable JS API Parameter and Unique Video ID for Video Tracking.*/
var n=0;

jQuery('iframe').each(function() {
 
 var src = jQuery(this).attr('src');
 
if(src){
	if (src.indexOf('youtube.com') > -1) {
     
    	if (src.indexOf('?') > -1) {
			if (src.indexOf('enablejsapi') == -1) {
				src = src + '&enablejsapi=1';
      		}
     	} else {
	 		src = src + '?enablejsapi=1';
    	}

		jQuery(this).attr('src',src);
		jQuery(this).attr('id','player' +n);
		n++
    }		
 }	   

});

Part 2: Code to Listen for the YouTube events and map them to _satellite.track calls

Here’s, it’s important to understand the values that the YouTube API passes back for the various video states:

  • -1 – unstarted
  • 0 – ended
  • 1 – playing
  • 2 – paused
  • 3 – buffering
  • 5 – video cued

We’ll reference these numbers in the code, so this will help you understand what is happening in the case statements you see. You can read the full YouTube API documentation here, but you don’t need most of it for this solution; feel free to reference if you want to customize further. You might also see the following in other code:

  • YT.PlayerState.ENDED
  • YT.PlayerState.PLAYING
  • YT.PlayerState.PAUSED
  • YT.PlayerState.BUFFERING
  • YT.PlayerState.CUED

These are identical to the above numeric values, and we’ll be using the numeric values only in this example.

Key things to know:

  • This example tracks 3 events: start, end, and pause. This code can be modified using the event codes listed above to add additional case statements as needed.
  • This code doesn’t collect video name but could easily be modified to do so using after reading through the YouTube API details, or you could grab the document.title from the overall window in a data element for later use, whatever makes the most sense for your use case.
  • This code example assumes there is always only 1 video per page, and that you are using the script above to insert the player names. That is why the name “player0” is hard-coded in. You can make this piece of the code dynamic to look for any player name according to the syntax you are using.
  • Auto-play videos sometimes play too soon to be caught immediately if the Adobe code takes time to load.
  • The channel name can be a name that you specify. In 2 locations, I have the placeholder text “channelnameofyourchoice.” Change this to a more meaningful name describing the type of videos your site hosts.

Without further ado, here is the second block to add just beneath the earlier one in the “Configure Tracker Using Custom Code” section of the Adobe Analytics extension.

/*Video Code Block 2 of 2: Add event listener and fire _satellite.track rules. */ 

try {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: 'channelnameofyourchoice'
            });

            iframe.contentWindow.postMessage(message, 'https://www.youtube.com');

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

addYoutubeEventListener(document.getElementById("player0"), function(e) {
    console.log(e)
    switch(e.info) {
        case 1:
            _satellite.track("video_start");
            break;
        case 0:
            _satellite.track("video_end");
            break;
        case 2:
            _satellite.track("video_pause");
    }
});}

catch(err) {console.log("Video Tracking Not Present");}

Part 3: Set up Rules in Adobe Launch to Capture The Direct Calls

Now that you have added the appropriate parameters to the video and captured the events from the video, you want to ensure these events make it into Adobe Analytics. For purposes of this post, we’re going to assume you have the ability to create new rules in your Adobe Launch environment, and that you’re an experienced Adobe Analytics administrator who has already set up the appropriate events for the different video states you’d like to track.

In my example above, I am sending video_start, video_end, and video_pause. I’ll need to create one rule for each.

Name each new Launch rule according to your rule naming convention. (What? you don’t have one? Jim and I agree on that as well – check out his post here. I tend to use a modification of this in a lot of my implementations using the same principles. He’s written them up in a concise way in this article.)

In the “IF” section, select “Core” as the Extension and “Direct Call” as the event type. In the “identifier” section, enter the same text you are sending in the _satellite.track calls in your code, for example video_start for the Video Start rule. Keep your changes and go back to the main rule.

In the Conditions section, you have the option to save yourself a lot of server calls (i.e. money). Do you really want to fire something every single time every visitor plays, pauses, or ends a video, especially if there are a lot of visitors and/or a lot of videos and/or really distracted visitors who pause a lot? If you only want to know whether something occurred at least once, you can set a condition using logic type Regular, the “Core” extension, and a “Max Frequency” condition type. You can set it to return true no more than once every “1” page views. This will prevent the call from going out over and over again if people are taking multiple actions. You’ll be able to see the difference if you look at your network calls before and after implementing the max frequency setting.

In the Actions section, you’ll want Adobe Analytics – Clear Variables, Adobe Analytics – Set Variables, and Adobe Analytics – Send Beacon.

  • In the Set Variables item, select the appropriate event to fire, and add any additional informational eVars you might want to configure for the video name, etc.
  • In the Send Beacon section, select the s.tl() option so that the event is not treated as a new page view. You can optionally enter a custom link name to describe the video action, but this is not required and is a bit duplicative.

Testing On Your Site

Build your changes into a development library. Navigate to a page where a video is present on your site, either on a page on your lower environment, or by going to your production site and using something like the Launch and DTM Switch Chrome extension (or the Charles Proxy Map Remote function if you’re feeling fancy or not using Chrome) to switch out the environment reference to use your development library on the prod site. Take various actions on the video while monitoring your network calls or looking in a debugger and ensure the events fire when you expect them to. If everything looks good, publish and re-test on production.

Final Thoughts

Our little experiment turned into something that can hopefully be of use to a larger audience. Play around and see what works for you!

 

13 thoughts on “Tracking YouTube Videos in iFrames with Adobe Launch: A Simpler Approach”

  1. Thanks for sharing this. I had a similar solution, though I didn’t have do what you did in Part 2. I also used Custom Events instead of Direct Calls.

    Like

      1. Hi PJ, “doesn’t work” is a broad statement. 🙂 I have this running in production across several sites, but each circumstance is unique. If you can be more specific about what’s not working and your environment, it will help with the quality and helpfulness of replies.

        Like

  2. Also for those who don’t want to use jQuery my teammate and I came up with a JS-only version of the first code block!

    document.querySelectorAll(‘iframe’).forEach(function(el) {
    var src = el.getAttribute(‘src’);

    if(src){
    if (src.indexOf(‘youtube.com’) > -1) {

    if (src.indexOf(‘?’) > -1) {
    if (src.indexOf(‘enablejsapi’) == -1) {
    src = src + ‘&enablejsapi=1’;
    }
    } else {
    src = src + ‘?enablejsapi=1’;
    }
    el.setAttribute(‘src’,src);
    el.setAttribute(‘id’,’player’ +n);
    n++
    }
    }
    });

    Like

    1. Awesome! I’ve been playing around with some other events as well, may be worth a follow-on. This post is a hotbed of innovation! 😀

      Like

  3. Hi Anne,

    thanks much for the wonderful post on tracking you tube through launch. I have implemented and tracking the events as mentioned. However, we also want to track few milestone like 25%, 50% and 75%. Would you be able to assist us to track the same. I have gone through the code and unfortunately it is bit difficult to understand for the people like me as a “non-dev” guy. Please let me know if any.

    Thank you,
    Jay.

    Like

    1. Thanks, glad you enjoyed it. The YouTube API contains items that track the total duration of the video (which becomes available shortly after play) and the time progressed so far, so it would be a matter of dividing time played by total duration and then writing some CASE statements or IF/THEN/ELSEs to pick up the 0.25, 0.5, and 0.75 values when they are reached. I may write that out in a follow up post on that, but I’d also encourage active playing around with the code in a controlled environment in the meantime. (It’s always ideal to know the ins and outs of code found online, and while mine is not malicious, I don’t necessarily want to encourage code use without simultaneously encouraging exploring and learning about what it does.)

      Like

  4. Hi Anne,
    This was really helpful . Thanks a lot. I would like to get clear on one point. When there are multiple iframe videos in the page, can you please guide me how to proceed. Because I couldnt see any attribute in iframe that helps me to identify which video is started.
    Thanks in advance!!!

    Like

    1. Glad you found it helpful! Thanks for reading. From the “Key Things to Know” section: “This code example assumes there is always only 1 video per page, and that you are using the script above to insert the player names. That is why the name “player0” is hard-coded in. You can make this piece of the code dynamic to look for any player name according to the syntax you are using.”

      Like

  5. Hi Anne ,
    Thanks a lot for the reply . I would like get one more suggestion from you , My page contains more than 1 ytube embedded video, and I could see that video_start is setting whenever I pause and resume my video . I want my direct cal to fire only if it is a video start . since my page contains more than one video, I couldnt apply max frequency in my direct call rule since it will affect other videos . Hope you got my query , if you could share some thoughts on this that would be really helpful for me.
    Thanks in advance!!

    Like

    1. You can set a flag that indicates when a video starts, and have all subsequent starts for a video of that name set as a resume event, setting the flag to false when the end of the video is reached. This is one of the other items I may or may not have time to write out on the blog as a follow up that’s mentioned in the original, but there you have it in words to mess around with!

      Like

Leave a comment