Tracking Form Submissions in Iframes – Google Analytics & Iframes, Pt 1



Be it for basic form submission, third party content, or even behind-the-scenes logging, iframes often play important roles in online user behavior. They provide a simple fix to many of the common snafus of the web – asynchronousity, portability, and cross-domain communication. They’re also the equivalent of a web analytics bear trap.

Here we see the Web Analyst, drunk on the power of Google Tag Manager, encounter a cross-domain form

In this series, I’ll address some common issues when Google Analytics and iframes meet, as well as solutions to those problems. In Part 1, I’ll be addressing the most common issue – tracking simple, one-off things like events or form submissions within iframes.

Part 2 will cover tracking more complex user behavior in iframes. Be warned: this is a very technical issue, so there will be some technical jargon. If the phrase ‘post a message to the parent frame’ frightens you, you might want to just go ahead and check out another post on our blog today.


For this to work, you need to be able to add unadulterated code on the iframe and the page the iframe is inserted on. If you’ve got a third-party service you’d like to track, and you can’t insert code snippets on their pages, this will not work and you’re out of luck. If you can’t add code to the iframe, you can’t measure interactions with it, period. Do not pass GO, do not collect $200. Sorry.

Using the postMessage API

The postMessage API is a browser API that allows developers to communicate between iframes and the HTML that contains them. Using postMessage, we can have our child iframe emit a message, which we can ‘listen’ for and use to notify GTM that an important interaction has occurred. This is great for tracking things like simple form submissions within iframes, which we’ll use for our example. We’ll need to take the following steps:

1.) Post a message from our child iframe
2.) Listen for the message in our parent frame
3.) When we catch the message, push an event into the GTM Data Layer

Let’s get started!

Posting a message

Emitting a message from an iframe is relatively simple with the postMessage API.postMessage is a method of our parent object. It requires two arguments: the message we’d like to post, which should be a string, and the targetOrigin for the message, which is the protocol, hostname, and port of the parent frame we’re trying to send the message to. If the values in the targetOrigin don’t match our parent frame, the message will fail, so make sure these are correct; if I were trying to post from an iframe on to the LunaMetrics site when a form was submitted, I’d use this code on

Inside of our iframe, we’ll want to put that code in a block that executes once our user action has taken place. If we’re trying to track a form submit, this can just be placed on the ‘Thank You’ page.

That’s it! Pretty simple, right? You can make things more complicated, of course, by doing fancy things like serializing data into JSON and de-serializing it in the parent frame. Experiment!

This only gets us 1/3rd of the way, though; we still need to listen for our message in our parent frame.

Listening for the message

Once we’ve started sending our message, we need to teach our parent frame to ‘listen’ for the message. Think of this like placing a phone call; our postMessage call is like dialing the number, and now someone needs to pick the phone up in order for us to talk to them. When we post our message, it generates a JavaScript Event in our parent frame. We can listen for it by attaching an Event Listener to the window object of the parent frame. There are some hoops we have to jump through in order to get this to work on older browsers; feel free to copy the below code for your own use:

Now we can listen for our message using the following code:

Acting on our message

Once we’ve got the code in place to emit and catch our message, it’s time to add some logic to handle the message whenever it shows up. In our example, we want to notify Google Tag Manager that a form has been submitted. How do we notify Google Tag Manager something has occurred? That’s right! We use dataLayer.push(). Great work, class.

Let’s step through the code above! First, we’re registering a listener for the message Event on our window. That’s how we catch the message once our iframe emits it.

Next, we’re safely instantiating our dataLayer variable. This is the tool we use to communicate with Google Tag Manager. Because our code could be moved around, we always instantiate the dataLayer variable with this special syntax. Read literally, it means ‘Define the phrase “dataLayer” to either be a reference to the dataLayer that already exists, or, if that doesn’t exist yet, define the global dataLayer to an empty array, then define the phrase “dataLayer” to refer to that newly created array.’ If that’s Greek to you, just nod and smile (and trust a stranger on the Internet, at least for today). If you’re not using GTM, you’ll just use the Universal Analytics syntax for an event here.

Once our dataLayer is available, we’re ready to handle our message. The string that we sent with our parent.postMessage call is stored in the data property of the message that we catch. We’ll access that property, and then we’ll check to make sure this is the right message for this piece of code; after all, we could be sending a bunch of messages from our child iframe, and not all of them signify a successful form submission. If the message is formSubmit, we push an event to the Data Layer, letting Google Tag Manager know our form was successfully submitted!

We’re also checking that the message is being emitted from where we expect it by checking the message.origin property. Just like with our targetOrigin, this must match exactly the protocol, hostname, and port of our child frame. In our example

Let’s put that all together now:

Ta-da! We’ve done it. Now in Google Tag Manager, all we need to do is fire our Google Analytics event using a Custom Event Trigger with the value formSubmit.


Once we’ve added our code snippets to our child frame and a parent frame, we can test that everything is working by using Debug Mode in Google Tag Manager. In the Preview pane, we should see the formSubmit message show up:

If there’s no message, check the Developers Console:

  • If you see a message like “Failed to execute ‘postMessage’ on ‘DOMWindow’: The target origin provided (‘THE_WRONG_HOSTNAME’) does not match the recipient window’s origin (‘YOUR_PARENT_HOSTNAME’)”, that means the code on your iframe has the incorrect targetOrigin. Check for typos!
  • If you see nothing, check that the code on your parent frame is checking for the right hostname of your child frame and the right message; again, this is fertile ground for typos.
  • If you’re still stumped, try adding console.log() statements in between all of the steps of your code. It could be that the iframe is not triggering the message at all, or that the parent frame isn’t catching the message in time. Logging to the rescue!

Tune in next time

This is just Part 1 of my two part series; next time, I’ll be handling what to do when you’ve got complex, multi-part user interactions within an iframe. Please subscribe to the blog and make sure you don’t miss it (I’ll also update this post with links to that one).

Are you having issues tracking things inside iframes with Google Analytics? Of course you are. Vent in the comments.

Dan Wilkerson is a former LunaMetrician and contributor to our blog.

  • Daniel Laidler

    Tell us more Dan. I have a third party payment gateway in a iframed modal window. It has four steps and I have a client that will kill for more insights

    • Dan Wilkerson

      Hi Daniel,

      Can you add code to the pages of that third-party payment gateway?


      • Daniel Laidler

        Unsure, but if I could run a vanilla version of your solution, it may be possible to have the vendor incorporate the code (as it would be an additional feature of their product).

        As there is financially sensitive information being transferred the embed script would need to be simple (for their devs to understand easily), while pushing generic step completion data back to the parent.

  • Daniel Laidler

    Dan, what happened to part 2?

    • Dan Wilkerson

      Hi Daniel,

      You may have already seen the links in the above!

  • Sasuke-Kun

    Helpful tutorial! Thank you πŸ™‚ I do have a problem though.. my iframe is from another domain:

    I can’t add any code in that third-party checkout page

    Is there a way to track my conversions if I embed this code (iframe) in my own domain (page)?

    In other words, my plan is to embed this code in my page (in my domain) and track my sales or track when people fill out their information but don’t buy.

    I’m using Facebook ads to promote my business, so I use the Facebook Pixel to track my conversions and events. If you know what specific steps I should start doing, it would be very helpful.

    Best Regards

    • Dan Wilkerson

      Then you can’t track it. Sorry!


      EDIT: For clarity, browser security policies prevent us from communicating or extracting data from cross-domain iframes. If you can’t add code to both domains, you can’t track the interaction, do not pass go, etc, etc.

  • Rich Cetta


    Thank you so much for this post as the solution within finally made it possible for us to enable tracking for our iframed forms. Our entire marketing division will be ecstatic as a result once I break the good news to them!

    I was wondering if from your posted examples there may be difference in the message posted and subsequently listened for? Per script in ‘Posting a message’ section: (postMessage = ‘formSubmission’) and Per script in ‘Listening for the message’ section: “if ( === ‘formSubmit'”

    It took me a while to test through as I am not proficient with JavaScript, however once I tweaked that it worked perfectly!

    Thanks again!

    • Dan Wilkerson

      Hi Rich!

      Good look; that’s a typo! Fixed.


      • Twan de Leijer

        Hi Dan,
        Great article! Thank you so much! Like Rich said, my marketing department here will be very glad πŸ™‚
        The typo is still in the section where you show the entire code for the parent frame (below “Let’s put that all together now:”). After changing that it worked like a charm!

        • Dan Wilkerson

          Oi vey, so it is. Greaaaat job on my part. I’ve updated them all to formSubmission, so it should be consistent. Thanks for pointing this out!

          • Twan de Leijer

            Thanks Dan. One additional question: does this work on all major browsers including mobile devices? Reason I ask is because I’ve tested it on my desktop as well as iPhone. It works on all my desktop browsers, but not on my iPhone (Safari) (at least GA is not picking up the event). Any idea?

          • Dan Wilkerson

            Hi Twan,

            That’s odd; Safari does support cross-domain iframe messaging. I’m 99% sure that I’ve used this API with Safari in a testing environment in the past, but it’s been awhile, so I can’t so with 100% confidence. Do you have a content blocker installed?

            EDIT: And I’ve tested this w/ BrowserStack from IE8+ with no issues, and Safari/Chrome/Firefox (desktop).


          • Twan de Leijer

            I don’t get it. I’ve tried it with Chrome for iOS and it works. All other desktop browsers that I’ve tested it on work. But just on Safari for iOS I’m not getting a response. I don’t have a content blocker installed. Other events are triggered (such a scroll depth trigger we have implemented). But the form submit event doesn’t come through..

          • Dan Wilkerson

            Is the scroll event coming from within the iframe? That’s really odd; unfortunately, I’m not able to take a close look at this at the moment, but I’m going to add it to my list. If you get to the bottom of it, I’d really appreciate any insight you can offer.

          • Twan de Leijer

            The scroll event is coming from the parent window.
            I have dived into the issue a bit more. I’m not a developer myself, but what I seem to have picked up on some blogs is that it has something to do with the focus of either the iframe or the parent ( I tried to add the parent.focus() and window.focus() to the code in the iframe, but no luck. Does this ring a bell to you?

  • Dan

    Can the post.message be added to the iframe through GTM (the third party has added GTM to their iFrame for me), or does it need to be placed directly within the iFrame code?

  • Julien

    Hi Rich, Thanks for this great post!
    I am the parent frame and I embed an child frame form from another domain.
    I want to track form completions (Thank you page).
    I am 99% sure, that this child iframe will be able to PostMessage (they have a great IT team)
    However, on my side, my website (parent frame) is a WordPress site and I am not a developer unfortunately. How could I simply “Listening for the message” and “Acting on our message” with WordPress ? Is there a Plugin for that or place where I can implement this code ?

    Thanks a lot,


  • I have a somewhat complex (for me) onsubmit i-frame requirement that I would
    rather sub out to an expert (and learn from that) than learning from
    trial and possible error on the client’s server. Are you amenable to
    smaller jobs like that? If so, happy to open an email dialogue where I
    can share the particulars. Thanks! -R

  • Julien

    Hello Dan.
    If I can put my GTM script on the Iframe pages (pardot) should I have to do all this?
    Or could I just listen to the submit form on my pardot page
    thanks a lot

Contact Us.

Follow Us



We'll get back to you
in ONE business day.
Our Locations
THE FOUNDRY [map] LunaMetrics

24 S. 18th Street
Suite 100

Pittsburgh, PA 15203


4115 N. Ravenswood
Suite 101
Chicago, IL 60613


2100 Manchester Rd.
Building C, Suite 1750
Wheaton, IL 60187