Google Analytics cross-domain tracking with Google Tag Manager – Part 3: Iframes

This blog post is obsolete. It is much easier to use window.postMessage to communicate between iframes on different domains. Pretty much all modern browsers can use postMessage without problems and in our tests this is working great. Google Analytics has pretty good documentation on how to implement postMessage for cross-domain tracking of iframes in Google Analytics. The code just needs to be adjusted slightly to use GTM instead of directly using Google Analytics, but this is easily done.

This is the last part in a three part series about how to do cross-domain tracking with Google Analytics when using Google Tag Manager for tag deployment.

[Edit on 17/12/2013: Corrected typo in code]

The first part can be found here: cross-domain tracking with Google Tag Manager – Part 1: Links
The second part can be found here: cross-domain tracking with Google Tag Manager – Part 2: Forms

To recap:
There are three ways to cross from one domain to another domain

  • A link that goes from domainA to domainB. Example code:
    <a href="http://www.domainB.com">link going to domain B</a>
  • A form on domainA sends its data to domainB. Example code:
    <form action="http://www.domainB.com">
  • An iframe on domainA that loads contents from domainB. Example code:
    <iframe src="http://www.domainB.com"></iframe>

All of these ways must be tracked to ensure consistent information. The previous blog posts in this series showed how to track links and forms cross-domain. So now we will have a look at how to track iframes cross-domain.

Prerequisite: Enable the linker in Google Analytics.
If you have not done so already you need to allow the linker in Google Analytics – both on the main website and in the iframe. Otherwise Google Analytics will not read the supplied cross-domain tracking information on the other domain. For instructions please check the first blog post about cross-domain tracking.

Tracking cross-domain iframes with Google Tag Manager

Cross-domain tracking is unfortunately a bit complicated for iframes:
Links and forms require an active user action and these actions usually happen after the GTM has loaded the Analytics tracking code. So it is no problem to append the tracking information to the links and form actions that go cross-domain.
Iframes work a bit different – they are usually automatically loaded when the page loads, which means that iframes might be loaded before the Google Analytics code is available.

The only way to ensure correct cross-domain tracking via iframes is to load the iframes via JavaScript once the Google Analytics code has loaded. However, this creates a dependency that might break your website – if the Google servers are down then the iframes will not load at all. The iframes will also not load if there was some error in implementing the steps outlined below, so keep that in mind and think about whether cross-domain iframe tracking is really required.

There are various ways to load the iframes via JavaScript. The approach outlined below is quite complicated – I hope that there is something easier around which I just have not found yet…

Step 1 – change the code of the website

A normal iframe usually looks like this in the source code:
<iframe src="http://www.domainB.com"></iframe>

Change this code such that it looks like this:

<iframe src="about:blank" data-iframe-src="http://www.domainB.com"></iframe>
<noscript><iframe src="http://www.domainB.com"></iframe></noscript>

The line
<iframe src="about:blank" data-iframe-src="http://www.domainB.com" />
loads the iframe with an empty url and the url that should actually be shown has been moved to a data attribute. Data attributes have been introduced with HTML5 and are a great way to store data in the HTML code that can then be read via JavaScript. If you need more information than read the W3C draft for HTML5 concerning data attributes.
The line
<noscript><iframe src="http://www.domainB.com" /></noscript>
serves as fall-back if there is no JavaScript. This means that, if JavaScript is disabled, the normal iframe will be loaded.

Note that the iframes will not load now until the other steps are completed!

Step 2 – set up a rule that triggers when both Google Analytics and the DOM is loaded

To correctly decorate the iframes it is necessary to wait for two events:

  • the HTML of the document (the DOM) must be loaded
  • the Google Analytics code must be loaded.

GTM already provides the event gtm.dom for when the DOM has finished loading. But we still need to a way to tell whether Google Analytics has loaded.

Note: Since Google Tag Manager loads the tags in the container asynchronously Analytics might be loaded before the DOM is completed or vice versa. There is no way to know in advance which event will happen first so it is necessary to explicitly check that both the DOM and the Analytics code are loaded.

The best way seems to be adding a hit callback to Google Analytics. A hit callback it some JavaScript code that is executed when Google Analytics makes a hit like a pageview. This can be used to push an event into Google Tag Manager – when this event happens then the Analytics code is definitely loaded.

Trigger an event when Google Analytics has loaded

To do that create a new macro with these settings:
Name: notifyGAHitCallback
Type: Custom JavaScript

function() {
  return function() {
      'event' : 'gaHitCallback'
    return true;

Google Tag Manager macro as Google Analytics hit callback

Then go to the Google Analytics tag that you use for your page view tracking and add this macro as hit callback – see the screenshots below.

For classic Analytics

GTM set up hitcallback in classic analytics

For universal Analytics

GTM - set up hit callback in universal analytics

Make a rule when both DOM and Google Analytics are loaded

Unfortunately there seems to be no way to set up a rule that checks two events at the same time. so a set up like
{{event}} equals gtm.dom
{{event}} equals gaHitCallback
will NOT work – there can be only one event checked at a time.

As a work-around we can check all events in the dataLayer – if both gtm.dom and gaHitCallback are in the dataLayer then we can trigger the iframe decoration.

To do that create a macro with the following settings:
Name: checkDomAndGALoaded
Type: Custom JavaScript

function() {
  var i = dataLayer.length,
      gaLoaded = false,
      domLoaded = false;
  while(i--) {
    if(dataLayer[i].event === 'gtm.dom') {
      domLoaded = true;
    if(dataLayer[i].event === 'gaHitCallback') {
      gaLoaded = true;
  return domLoaded && gaLoaded ? '1' : '0';

GTM macro for checking dom load and Google analytics load

This macro will return ‘1’ if the events gtm.dom and gaHitCallback can both be found in the dataLayer.

Then create a rule with these settings:
name: domAndGALoaded
{{event}} matches RegEx (gtm\.dom|gaHitCallback)
{{checkDomAndGALoaded}} equals 1

Google Tag Manager rule to check whether dom is loaded and Google analytics is loaded

This rule will trigger when both events have happened, meaning the HTML code has finished loading and the Google Analytics code is there as well. Now the iframes can be properly decorated.

Step 3 – go through all iframes and set the source with cross-domain parameters

Add a custom HTML tag with the content below. This code first checks if there is classic or universal Analytics loaded and gets the information to use linking from the first tracker. Then goes through all iframes with the structure mentioned in step 1, adds the cross-domain information to the source url and loads the iframe with this url.

Note: If both classic and universal Analytics are used on the website it will automatically add the cross-domain information for both. However, it will only take the first tracker of each type, so if there are e.g. multiple trackers for universal Analytics then only the first one will be used for the cross-domain tracking.

(function() {
  var iframes = document.getElementsByTagName('iframe'),
         i = iframes.length,
         iframe, src, uaLinker, gaLinker, trackers;
  //if universal analytics
  if(typeof ga !== 'undefined' && typeof ga.getAll === 'function') {
    trackers = ga.getAll();
    if(trackers.length) {
      uaLinker = new window.gaplugins.Linker(trackers[0]);
  //if classic analytics
  if(typeof _gat !== 'undefined' && typeof _gat._getTrackers === 'function') {
    trackers = _gat._getTrackers();
    if(trackers.length) {
      gaLinker = trackers[0];

  //now loop through all iframes, decorate the source url and remove the display none
  while(i--) {
    iframe = iframes[i];
    if(iframe.src === 'about:blank' && iframe.getAttribute('data-iframe-src')) {
      if(uaLinker) {
        src = uaLinker.decorate(src); 
      if(gaLinker) {
        src = gaLinker._getLinkerUrl(src);

The tag needs to be triggered on the rule set up in step 2.

Google Tag Manager: tag for tracking iframes cross-domain

Note: Google Tag Assistant shows an error that the function _getTrackers() is unknown. Just ignore this error – the function definitely exists, it is just not official. There is no other way to get all used trackers in classic Analytics.

Google Tag Assistant error for _getTrackers()
If you only use Universal Analytics then all the parts concerning classic analytics can be removed from the tag, in this case the error will not appear anymore.

Optional: As fall-back automatically trigger the iframe decorator tag after 300ms

If the loading of the iframes is highly important for the website then you might want to make sure that they load even if the Google Analytics code does not load.

To do that add a timing tag that triggers an event after 300ms after the DOM has loaded. Of course you can change the time to whatever you deem suitable.

Settings for the tag:
Name: Timer 300ms after DOM
Tag Type: Event Listener > Timer Listener
Event name: timer300msAfterDom
Interval: 300
Limit: 1

triggering rule:
{{event}} equals gtm.dom

GTM timer tag for 300ms after DOM load

Then set up a new rule that triggers when the timer triggers:
{{event}} equals timer300msAfterDom

GTM rule that triggers after 300ms

Then add this rule to the rules used for triggering the tag in step 3:

GTM - add timer rule to cross-domain iframe decorator tag

This way the iframes will be loaded no matter whether the Google Analytics code is already there or not.

Note: This setup still relies on Google Tag Manager, so if that is too risky as well then you need to add the code with a small timeout directly on your website.

Step 4: Preview and test

Keep in mind that you need to change the HTML structure of your iframes, so this should ideally be tested on a development version of the website.

To test the tag load the preview version and wait until the iframes have loaded. Then use e.g. ‘inspect element’ in Chrome or Firefox to see the generated source url of the iframe.


  1. Muneeb says:

    This is great information, GREAT GREAT information. I was trying to track the embedded iframe for vimeo videos for our new website, and did not want to get the IT involved. This article helped me greatly. Thank you so much for sharing it. I will definitely recommend my company to start using knewledge platform and services.

    1. Claudia says:

      Hi Muneeb,

      Sorry for the late reply. Vimeo iframes are on a different domain, so unless vimeo loads your Analytics code on their domain the cross-domain tracking will not be of much use. However, it should be possible to use a custom HTML tag in GTM to change the source url of the vimeo iframes such that their API is loaded – then you could use another custom HTML tag to hook into the various events that the API provides: http://developer.vimeo.com/player/js-api
      If I ever find the time I will test this and explain in a blog post about how to do this with GTM, but no promises…

      There is already a script that tracks vimeo iframes with GA on GitHub, so if you know a bit of JavaScript then you can easily adjust this so that it works with GTM.

      As for your question concerning where the data is displayed: This depends on which type of tracking tag you trigger with GTM. For videos I would usually use event tracking, so in this case the data would appear under events.

      Good luck


  2. Johny says:

    I don’t think I have enough experience to judge whether this is a solution for my specific issue, but I would like to explain ti here, hopefully somebody can help.

    I have a wellness website on a .de domain, and then I have a booking system, provided by one US company, on .com domain and embedded into iFrame. The US company allows to put GA code so this is working and I’m definitely getting data.

    But I noticed in Google Analytics that once the user goes to iFrame, it basically registers as new visit, classify it as another referral, coming from my website.

    The goals which I set in GA, tracking when users finishes booking process, are registered but when I import them to Google Adwords, as conversions, Google Adwords doesn’t see any data. I assume that the problem is with iFrame and somehow Google Adwords doesn’t register that as conversion, probably thinking that users have left the site.

    Any clue what could be solution?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.