If you have worked with a website that uses iframes, then you probably know the tracking pain points that arise. Generally, you’ll encounter one or all of the following:
- Campaign/Referral attribution loss
- Session restarts/dropouts
- Double page views
- Inaccurate bounce rate
- Cookie/Browser storage access
The issues you encounter depend on how your iframes are set up; for instance, are they on the same domain, sub-domain or perhaps hosted on a completely different domain? What does the user do at the end of the process? Does a third-party service even allow you to add tracking?
The solution is postMessage. This supported browser functionality allows you to send messages between both the iframe and parent window/main website, in most cases circumventing all the problems you would face traditionally with tracking iframes.
While in essence, it sounds fairly straightforward, there are intricacies with the set-up and it’s development-heavy. So put on your coding cap, and ready your copy and paste, as it's time to fix your iframe tracking.
Please note, this solution assumes that coding changes can be made to your iframe. While the set-up is implemented via Google Tag Manager, the general concept and JavaScript can still be applied directly.
Tracking iframes in GA using Google Tag Manager
The Concept
- Tracking in the iframe will use postMessage to send data to the main website.
- The main website will be listening for messages posted from the iframe.
- The main website will convert these received messages into analytics tracking calls.
The great aspect of this solution is that the iframe simply sends messages to the main website, no tracking scripts are called within the iframe. The main website, where the tracking takes place, will not encounter session, attribution or access issues while providing more control of when to fire specific tracking calls for the iframe content.
The Set-up
- Google Tag Manager is implemented on the main website.
- Google Analytics is implemented on the main website.
- A separate Google Tag Manager container is implemented in the iframe.
The Solution
iFrame setup
- Create a Custom HTML Tag: Workspace > Tags > New > Choose tag type > Custom HTML
- Copy & paste the below code snippet into the Custom HTML Tag
- Add the All Pages Trigger: Tag > Triggering > All Pages
- Save & Name the Tag: cHTML - postMessage - Send - Page View
Code snippet:
<script>
(function(){
try {
if(typeof parent != "undefined" && parent != window) {
if(typeof parent.postMessage != "undefined") {
var message = {};
message["origin"] = {
"type" : "iframe",
"host" : {{Page Hostname}},
};
var event = "custom.postMessage";
// Add description of the event
event += ".page";
message["event"] = event;
// Add custom data
message["url"] = {{Page URL}};
// Convent message into a string
var messageJSON = JSON.stringify(message);
//Send message to parent
parent.postMessage(messageJSON, "*");
}
}
} catch(err){if({{Debug Mode}}) console.log(err);};
})();
</script>
The above code snippet is designed to run within the iframe. It first checks to see if a parent exists (parent reference indicates the document is a child and/or iframe). Then the functionality of postMessage is confirmed to be available. Next a message is created; in this example we are creating a simple page view. The message object is then converted into a string to allow complex data to be passed across and the message is sent.
Main website set-up
- Create a Custom HTML Tag: Workspace > Tags > New > Choose tag type > Custom HTML
- Copy & paste the below code snippet into the Custom HTML Tag
- Add the All Pages Trigger: Tag > Triggering > All Pages
- Save & Name the Tag: cHTML - postMessage - Receive
Code snippet:
Code snippet:
<script>
(function(){
try {
var receiveMessage = function(event) {
try {
if(event && typeof event.data != 'undefined') {
var message = JSON.parse(event.data);
if(message && typeof message.event != 'undefined' && message.event.indexOf('custom.postMessage') >= 0) {
if(dataLayer) {
dataLayer.push(message);
}
}
}
} catch(err) {};
};
if(typeof window.addEventListener !== 'undefined') {
window.addEventListener('message', function(e) {
receiveMessage(e);
});
} else if (typeof window.attachEvent !== 'undefined') {
window.attachEvent('on' + 'message', function(e) {
receiveMessage(e);
});
}
} catch(err){if({{Debug Mode}}) console.log(err);};
})();
</script>
This snippet is set up to receive message events. It is important to note that the event listener functionality is not specifically targeted to the iframe events only. It’s possible that these listeners will pick up other message events from existing website functionality.
To counter this potential impact, you can see that within the receive function, conditional checks are used to validate the message received, and only react if it is indeed the event we specified from the iframe.
Upon validating the message, the message data is converted back into an Object and is then pushed into the main website dataLayer.
Completing and Tracking the Set-up
Now that we have the message entering the dataLayer, we simply need to create the required variables, trigger and tag.
Create the Variables
Workspace > Variables > User-Defined Variables > New
DLV - url
This is used to extract the URL in the dataLayer from the posted message.
Configuration | Value |
---|---|
Choose variable type | Data Layer Variable |
Name/Title | DLV - url |
Data Layer Variable Name | url |
URL - dataLayer - Path
This is used to extract the page path only from the DLV - url variable.
Configuration | Value |
---|---|
Choose variable type | URL |
Name/Title | URL - dataLayer - Path |
Component Type | Path |
More Settings > URL Source | {{DLV - url}} |
Create the Trigger
Workspace > Triggers > New
CE - postMessage - Page This trigger will fire when the dataLayer receives the postMessage.
Configuration | Value |
---|---|
Choose trigger type | Custom Event |
Name/Title | CE - postMessage - Page |
Event name | custom.postMessage.page |
Use regex matching | True |
This trigger fires on | All Custom Events |
Create the Google Analytics Tag
Workspace > Tags > New > Google Analytics
GA4 - page_view - postMessage
Configuration | Value |
---|---|
Choose tag type | Google Analytics: GA4 Event |
Name/Title | GA4 - page_view - postMessage |
Measurement ID | Set the appropriate Measurement ID |
Event Name | page_view |
Event Settings Variable | Select your Event Settings Variable |
Event parameter | Add any appropriate Event parameters as required |
Triggering > Choose a trigger | CE - postMessage - Page |
Final Thoughts
It’s worth noting that postMessage is a two-way street; it's actually possible to pass messages from the main website back to the iframe. You can then think of this set-up in reverse. For example, an iframe sends a message to the main website declaring it's ready. The main website reacts to this message, sending a message back containing information such as the user's client id (cross-domain tracking), instructions to trigger specific events, or other information that needs to be persisted. However, it’s worth mentioning that this approach is still not desired mainly due to restrictions in IOS that prevent the setting of third-party cookies when using iframes with different domains.
While this solution is fairly robust, it does assume that the main website will be loaded and have called the listener scripts before the child iframe loads and sends its message. This should be the case for the majority of set-ups; if you do notice missing pages then it might be worthwhile adding in logic to ensure both the main website and iframe are loaded before attempting to send messages.
This set-up is only scratching the surface of postMessage. In this example, we have only passed a Pageview from the iframe to the main website. It is possible to use the same methodology to track events and other key calls to actions that occur within the iframe. An ultimate solution would be to monitor every dataLayer event within the iframe and pass the messages across to the main website.
While this set-up is advanced, it will drastically improve the data quality and collection. When implemented correctly, it will circumvent many of the issues presented with iframes and provide greater control of how and when tracking occurs.
So, there we have it; a way to track iframes with Google Tag Manager. If you're new to GTM, or want to up your data analysis game, check out our Google Tag Manager training course.
This post first appeared on the Data Runs Deep blog. Data Runs Deep joined the Jellyfish global family in 2020 and the knowledge-sharing continues under the Jellyfish banner in Australia and around the world.