Pick the right tools

Tracking iframes with Tealium

Figure out how to track iframes with Tealium in a flexible manner.

Occasionally when you're setting up all your tracking requirements for your website, you may stumble upon some obstacles that seem hard to solve. Encountering iframes is likely one of these obstacles. They are definitely confusing if you're running into them for the first time, but let's explore how to track them easily and efficiently. This post will be quite technical, but you don't have to be a programmer to get through this, you can copy and paste the provided code snippets, and you just need to adjust a few variables.

Approach

Initially, you may consider adding Tealium to your iframe, and making sure that it is sending hits to all the tags you have added to your profile. However, we'll be using a different approach. We still need to add Tealium to the iframe, but we're going to use Tealium to push pageviews and events to the parent page (the page hosting the iframe). So let's say page A is loading iframe B, but instead of iframe B sending hits to tags directly, it will send hits to page A, and page A will then send the hits to the tags configured in that Tealium profile. This way, you will only have your tags configured in one profile, instead of your tags being spread across multiple profiles.


There are two options for your Tealium setup: either you use the same Tealium profile on your website and the iframe, or you use a separate Tealium profile in the iframe (recommended).


The problem with using the same profile is that iframes will likely be loaded on multiple domains for different websites. Even if that's not the case, you'll have to constantly consider the iframe when building load rules or adding tags, since you don't want your tags to load within the iframe.

Setup

Let's assume we're using 2 different Tealium profiles: the "parent" profile will be loaded on the main website (or parent), and the iframe (or child) will be using "child" profile. We will need to make changes in both profiles to make the tracking work.


We have already determined that we will use the child profile to send events to the parent profile. So the parent profile will have 2 tasks:


  • Receiving events from the child profile (iframe).
  • Letting the child profile know that it is ready to receive events.


This first task is obvious, but let's see why this second task is necessary. When a website is loading an iframe, it is possible that the iframe will finish loading before the website itself. This means that the child profile would load before the parent profile. Of course, this is not always the case, it is also possible for the parent profile to load before the child profile. This is important to consider because the child profile should not send events to the parent profile before it is ready to receive them.


The child profile will also have some tasks it needs to accomplish:


  • Measuring events.
  • Waiting for the parent profile to be ready to receive events.
  • Sending events to the parent profile.


Of course, we need to measure events. This could be as simple as measuring pageviews, but can be extended to any event like form tracking, button clicks, etc.. When an event occurs, we have to be sure that the parent profile is ready to receive them, so we must wait if necessary. When the parent profile is ready, we just need to push the event to the parent profile, and we're finished.

Code: the child profile (iframe)

All of these tasks can be boiled down to two JavaScript extensions. One extension on the parent profile, and one extension on the child profile. Let's have a look at the extension for the child profile. We'll start from the child profile because that's where the events are generated:

//Child (iframe) extension
(function() {
    // Configuration
    var PARENT_ORIGIN = 'https://parent.example.org';
    var SEND_PAGEVIEW = true;

    // ==============================================================
    // Do not edit below this line unless you know what you're doing.
    // ==============================================================
    var pageviewSent = false;
    var started = false;
    var eventQueue = [];
    window.utag_iframe = {
        view: onView,
        link: onLink
    };
    if(SEND_PAGEVIEW){
        eventQueue.push(utag_data);
    }

    window.addEventListener('message', onMessage, true);
    sendHandshake();

    function onView(dataLayer){
        eventQueue.push({
            event: 'view',
            data: dataLayer
        });
    }

    function onLink(dataLayer){
        eventQueue.push({
            event: 'link',
            data: dataLayer
        });
    }

    function sendEvent(event) {
        var data = {
            tealium_event: event
        };
        window.parent.postMessage(data, PARENT_ORIGIN || '*');
    }

    function onMessage(message){
        if(message.data && message.data.tealium_handshake === 'parent'){
            startSendingEvents();
        }
    }

    function startSendingEvents(){
        if(started){
            return;
        }
        started = true;

        eventQueue.push = function(event){
            sendEvent(event);
        };
        for(var i=0; i<eventQueue.length; i++){
            (function(event){
                setTimeout(function(){
                    sendEvent(event);
                }, i*100);
            })(eventQueue[i]);
        }
    }

    function sendHandshake(){
        var data = {
            tealium_handshake: 'child'
        };
        window.parent.postMessage(data, PARENT_ORIGIN || '*');
    }
})();

I know, it's quite a handful... Don't be alarmed though, this code takes care of a bunch of things for you, so you don't have to get your hands too dirty! At the top of the code, however, there are 2 things you still have to adjust. Let's check it out:


var PARENT_ORIGIN = 'https://parent.example.org';
In this line, simply replace the URL with the URL of the parent website, but do not include the page path.


Additionally you can choose not to send the initial page view (based on utag_data) when the iframe loads by setting SEND_PAGEVIEW to false. Simply replace the words "true" with "false" (without quotes).

Code: The parent profile

Now that the extension is set up to send events to the parent profile, let's jump into the parent profile's extension to receive the events and send them on their way to our favourite tags:

//Parent extension
(function() {
    // Configuration
    var CHILD_ORIGIN = 'https://child.example.org';
    var IFRAME_ID = 'my-iframe'; //HTML ID of the iframe

    // ==============================================================
    // Do not edit below this line unless you know what you're doing.
    // ==============================================================
    window.addEventListener('message', function(message) {
        var data = message.data;
        if (data.tealium_event) {
            utag.track(data.tealium_event);
        }else if(data.tealium_handshake === 'child'){
            sendHandshake();
        }
    }, true);
    sendHandshake();

    function sendHandshake(){
        var iframe = document.getElementById(IFRAME_ID);
        var data = {
            tealium_handshake: 'parent'
        };
        iframe.contentWindow.postMessage(data, CHILD_ORIGIN);
    }
})();

This code will simply listen for any events coming from the iframe, and trigger utag.view or utag.link accordingly. Here too, you have to set up some configuration:


At the top of this code, you should set the value of CHILD_ORIGIN to the URL of the iframe, just like you've done for PARENT_ORIGIN in the iframe extension. These origin configurations are necessary to make sure that malicious sites loading your website will not receive any events.


You also have to set the IFRAME_ID to the HTML ID of your iframe. So in the HTML you should see something like id="some-iframe" for your iframe. In this case you should set the variable as IFRAME_ID="some-iframe".

Finishing up

Once these configurations are done, you're free to publish your setup. However, if you chose not to send the initial pageview from the iframe, no hits will be sent yet. Within the child profile you should still add events if you want to track them, but instead of pushing them to utag.view and utag.link, push them to utag_iframe.view and utag_iframe.link. These are 2 functions that were created in the child extension within the iframe. If you're not familiar with JavaScript, this is something your website developer will have to do.


As always, make sure you test that everything is working before publishing to production.