To track iFrames, you usually need access to them. You’d insert a specific tracking code or a separate GTM onto the iFrame site. This sends a JavaScript message that your main website can listen to, allowing you to track iFrame events. But often, we can’t access the iFrame to add this code. To solve this challenge, here’s a simple solution. In this guide, I’ll demonstrate how to track interactions and form submissions within an iFrame even when you can’t access it directly.
iFrame form Tracking DataLayer Code
/**
* Author: Md Hasanuzzamna
* Linkedin: https://linkedin.com/in/md-h
* Youtube: https://youtube.com/@leomeasure
* Email: info@leomeasure.com
*/
(function() {
function leoMeasureIframeFormSubmitDataLayer() {
var iframeSelector = 'iframe'; // Change as your iframe selector example: 'iframe#id-of-iframe'
var iframe = document.querySelector(iframeSelector);
var isFormSubmitted = false;
var isInsideIframe = false;
var isCodeExecuted = false;
var iframeHeight;
var observer = new MutationObserver(function (_mutationsList, observer) {
var currentHeight = iframe.offsetHeight;
var iframeHeightChange = Math.abs(((currentHeight - iframeHeight) / iframeHeight) * 100);
if (!isFormSubmitted && iframeHeightChange > 40) {
observer.disconnect();
isFormSubmitted = true;
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'iframe_form_submit',
form_location: window.location.href,
iframe_id: iframe.getAttribute('id'),
iframe_class: iframe.getAttribute('class')
});
}
});
function handleMouseOver(event) {
if (event.target.closest(iframeSelector)) {
isInsideIframe = true;
} else {
isInsideIframe = false;
}
}
function handleFormSubmission() {
var formInsideIframe = iframe.contentDocument.querySelector('form');
formInsideIframe.addEventListener('submit', function (event) {
var formData = {};
var formInputs = formInsideIframe.querySelectorAll('input, select, textarea');
for (var i = 0; i < formInputs.length; i++) {
var input = formInputs[i];
if (input.type === 'radio') {
if (input.checked) {
formData[input.name] = input.value;
}
} else if (input.type === 'checkbox') {
if (!formData[input.name]) {
formData[input.name] = [];
}
if (input.checked) {
formData[input.name].push(input.value);
}
} else {
formData[input.name] = input.value;
}
}
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'iframe_form_submit',
form_location: window.location.href,
iframe_id: iframe.getAttribute('id'),
iframe_class: iframe.getAttribute('class'),
user_inputs: formData
});
});
}
document.addEventListener('mouseover', handleMouseOver);
window.addEventListener('blur', function () {
if (isInsideIframe && !isCodeExecuted) {
isCodeExecuted = true;
document.removeEventListener('mouseover', handleMouseOver);
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'iframe_form_start',
form_location: window.location.href,
iframe_id: iframe.getAttribute('id'),
iframe_class: iframe.getAttribute('class')
});
if (iframe.contentDocument) {
handleFormSubmission();
} else {
iframeHeight = iframe.offsetHeight;
observer.observe(iframe, { attributes: true, childList: true, subtree: true });
}
}
});
}
leoMeasureIframeFormSubmitDataLayer();
})()
hello. thank you very much for your effort. i’m sure you saw under your video on youtube that only the first trigger works for most people. i think it’s because certain behaviours are different when sending the form. a library with different triggers would be enormously helpful there.
I have tried several things and they don’t work. but I don’t know my way around either.
if (!isFormSubmitted && iframe.contentDocument.body.innerHTML.includes(‘Vielen Dank’)) {
observer.disconnect();
if (!isFormSubmitted && iframe.contentDocument.querySelector(‘.sc-e2065d79-0.hYcmCS.flex.justify-center.rounded-full.items-center’)) {
observer.disconnect();
and like 20 others hahahhaha. If you can look into this, I’d be so happy