Cross-Domain Communication: Parent Window and Child IFrame

Eric Crooks
9 min readMay 30, 2018

--

Overview

In this article, you’ll learn how to successfully allow a child iframe to send its parent window some data via JavaScript and jQuery event handling. You will learn how to create the cross-domain communication using two JavaScript functions: EventTarget.addEventListener() and window.postMessage().

You can view a working demo of the code explained in this article at https://crosscomm.app/. Make sure you open up your browser’s console to see the message being sent from the child iframe to the parent window.

You can clone the parent window code from: https://github.com/crookse/cross-comm.

You can clone the iframe window code from: https://github.com/crookse/cross-comm-iframe

Enjoy!

Table of Contents

  • Recommended Reading
  • Example Situation
  • Section 1 — Code The Markup
  • Section 2 — Enable Cross-Domain Communication
  • Section 2.1 — What You’ll Need
  • Section 2.2 — Using parent.postMessage()
  • Section 2.3 — Using window.addEventListener()
  • Section 3 — Exercising The Code
  • Section 4 — parent.postMessage() Errors
  • In Summary
  • Special Thanks

Recommended Reading

  • MDN web docs: Same-origin policy
    If you’re not familiar with this policy, then I recommend that you read this because it’s related to what you’re about to read in my article.
  • MDN web docs: EventTarget.addEventListener()
    Read this if you’re not familiar with how a browser window can receive a message via window.postMessage() and handle it using window.addEventListener().
  • MDN web docs: Window.postMessage()
    Read this if you’re not familiar with the window.postMessage() function and how it enables cross-domain (a.k.a. cross-origin) communication — allowing messages to be sent from one window to another window.

Example Situation

You’re a software engineer working on a web app and your product manager has tasked you with the following:

  • Insert an iframe on dev.project.com.
    Note: In this case, dev.project.com is known as the parent window for the iframe. The iframe is known as the child window (a.k.a. the child iframe) of the parent window.
  • The iframe will point to dev.iframe.com. You will create the content for this iframe (see next bullet).
  • The iframe will contain a form with two input fields that a user can use to enter his/her age and height; and the user will be able to submit the form using a green “Send” button.
  • When the form is submitted, JavaScript and jQuery will be used to handle the form submission — sending the user’s age and height from the iframe to its parent window (dev.project.com).

Section 1 — Code The Markup

Alright, so you’ve read your task and the first thing you do is code the HTML. You add an iframe to the index page for dev.project.com and the iframe’s src attribute points to dev.iframe.com. The next thing you do is add the form, input fields, and “Send” button to the index page for dev.iframe.com. This is what you have so far:

… and this is what the UI looks like:

The parent window (dev.project.com) with its child iframe (dev.iframe.com) — ready to be used for cross-domain communication. See the complete, working demo at http://cdc-project.crookse.com.

Yaaaaaay! All looks good and now you’re ready to enable cross-domain communication between dev.project.com and dev.iframe.com using JavaScript.

Section 2 — Enable Cross-Domain Communication

Coding the markup was pretty straightforward. You’re now thinking about how to create the cross-domain communication, but you’re not sure where to start. You do some Googling and find some resources on enabling cross-domain communication, but you’re not really understanding what’s being explained in those resources. Don’t worry. I got your back.

Section 2.1 — What You’ll Need

  • parent.postMessage() in the iframe.
  • window.addEventListener() in the parent window.
    Note: If you’re supporting IE versions before IE 9, then you’ll have to check if addEventListener exists. If it doesn’t exist, then you’ll have to use attachEvent. Confused? It’s all good. I’ll show you how.

Something You Should Know
Before enabling cross-domain communication between a parent window and a child iframe, you must know that it will only work if the same-origin policy isn’t being violated. In this case, the parent window’s domain (dev.project.com) is on a different domain than the iframe’s domain (dev.iframe.com). This means the same-origin policy will be violated. This doesn’t mean “hacky” code will need to be in place though. I’ll explain below how to make the cross-domain communication work between these different domains and why it works.

Section 2.2 — Using parent.postMessage()

parent.postMessage() is the function that allows the sending of messages, or data, from one window to another window. In this case, we want an iframe (dev.iframe.com) to send data to its parent window (dev.project.com). It takes at least two arguments to work properly:

  • The message (or data); and
  • The parent window’s protocol and domain name.
    Note: This argument is the reason why the cross-domain communication will work in this case. Providing this argument lets the parent window know that its child iframe knows (1) who its parent is and (2) the origin of its parent. In Section 4 below, you can see what happens when this argument is not specified or if this argument is specified incorrectly.

Below is a Gist of example code showing how parent.postMessage() can be called:

The first argument (the message) can be a string, object, array, or other data member.

Note: Since you’re trying to send the user’s age and height, you should probably send that data in an object for ease of use. However, feel free to do it your way while reading this article.

The second argument is the parent window’s protocol and domain name.

Note: The wildcard (“*”) can also be specified as the second argument; however, it’s not secure and I don’t recommend using it. You can see what it looks like in the below Gist though.

Note: dev.iframe.com CANNOT use parent.location.origin as the second argument in the parent.postMessage() call because it doesn’t share the same origin as its parent. If you try to do this, then your script will blow up and the following error will occur in the console (note continues after image):

Uncaught DOMException: Blocked a frame with origin “{origin}” from accessing a cross-origin frame.

(note continued) parent.location.origin works if the iframe shares the same domain as its parent. Please remember that. It will save you a bunch of headaches when you’re trying to figure out why parent.location.origin isn’t working for you. There are some examples on the Internet showing how to create cross-domain communication and they use parent.location.origin, but they don’t explain why parent.location.origin works.

So, what do we know and have so far?

  • We know the user’s age and height needs to be sent from the iframe to its parent window when the form is submitted (when the user clicks the “Send” button).
  • We know parent.postMessage() allows an iframe to communicate with its parent window.
  • We know the protocol and domain name of the iframe’s parent window, which must be provided as the second argument in parent.postMessage().

You know what the above list means? It means we have all of the things to write a complete parent.postMessage() function call for the iframe to use!

… And here’s what we have now for the iframe’s markup:

Note: Read the comments in the above Gist to get a better understanding of what’s going on in the code and how the default form behavior is prevented and replaced with the parent.postMessage() call. You can also see what the wildcard method looks like.

Section 2.3 — Using window.addEventListener()

Alright, we’ve learned how to send a message from the child iframe to the parent window using parent.postMessage() in Section 2.2. Now, we have to allow the parent to receive that message. We do this by using window.addEventListener() (and/or window.attachEvent() to support IE versions before IE 9).

window.addEventListener() is the function that allows one window to listen for messages, or data, being sent to it from another window. In this case, we want the parent window (dev.project.com) to listen for messages from its child iframe (dev.iframe.com).

Below is a Gist of example code showing how window.addEventListener() can be written (and shows how to support IE versions before IE 9):

Note: Read the comments in the above Gist to get a better understanding of what’s going on in the code and how the data from a message can be accessed and handled.

So, what do we know and have so far?

  • We know the parent window needs to receive the iframe’s message — the user’s age and height.
  • We know window.addEventListener() allows a window to receive messages from other windows.
  • We know how to access the message received from other windows (event.data).

You know what the above list means? It means we have MOST of the things to allow the parent window to handle messages from the iframe.

What are we forgetting? We’re forgetting some security measures. The above Gist shows how a window can receive messages from other windows, but it doesn’t show how to receive a message from windows of a specific domain (a.k.a. origin). In this case, we only want to handle messages received from dev.iframe.com, so let’s sexify the handleMessage callback and add some security measures — only allowing dev.iframe.com messages to be received and handled. Let’s also add some jQuery to the parent window to show that it received the user’s age and height by filling in some new DOM elements and adding some sexy DOM manipulation for a cooler UX.

… And here’s what we have now for the parent window’s markup:

Section 3 — Exercising The Code

What’s next? Well, we’ve coded all of the things required from the task list. That means all we have left on our list is testing the feature to make sure it works as expected.

So, does your implementation work as expected? Remember, I have a working demo of the code at http://cdc-project.crookse.com/ and you can clone the code from https://github.com/crookse/cdc-parent-window-child-iframe. Use these resources to your advantage.

Below is what my demo code looks like when I enter an age, enter a height, and press the Send button:

Cross-domain communication (from child iframe to parent window) successfully enabled. See the complete, working demo at http://cdc-project.crookse.com.

Section 4 — parent.postMessage() Errors

Below are some errors you might encounter when trying to enable cross-domain communication.

If parent.postMessage() is called without specifying the parent domain as the second argument, then the message will not be sent and an error similar to the one below will be displayed in the console:

Uncaught TypeError: Failed to execute ‘postMessage’ on ‘Window’: 2 arguments required, but only 1 present.

If parent.postMessage() is called and the wrong parent domain is specified, then the message will not be sent and an error similar to the one below will be displayed in the console:

Uncaught DOMException: Failed to execute ‘postMessage’ on ‘Window’: Invalid target origin ‘{origin}’ in a call to ‘postMessage’.

If parent.postMessage() is called and parent.location.origin is specified as the second argument when the window calling parent.postMessage() does not share the same domain as its parent window, then the message will not be sent and an error similar to the one below will be displayed in the console:

Uncaught DOMException: Blocked a frame with origin “{origin}” from accessing a cross-origin frame.

In Summary

I hope you learned something from this article. Cross-domain communication was a difficult beast for me to tackle, so I can understand your frustrations if you don’t understand it entirely. Just remember the following:

  • parent.postMessage() allows a window to send other windows a message.
  • window.addEventListener() allows a window to receive messages from other windows.

These two functions are all you need to enable cross-domain communication between a parent window and a child iframe.

GLHF.

Special Thanks

Adam Daniels
Back when I was working for Payoneer Escrow (Armor Payments at the time), Adam Daniels, the lead engineer, wrote cross-domain communication code for the Payoneer Escrow lightboxes. The code was 90ish% stagnant for a while until I was tasked to refactor all of the HTML, CSS, and most of the JavaScript. Reviewing the cross-domain communication code was one of the first things I did during that task. I mean… I had to refactor most of the JavaScript anyways, so why not start with a subtask where I could learn about some pretty good shiz right? I learned A TON from that subtask; and now I’m able to share and explain what I’ve learned. If Adam never wrote that code, then I probably wouldn’t have known about cross-domain communication right now; and this article wouldn’t exist. So, thank you, Adam!

Adam Hartz
Adam (I know… another Adam… and he worked at Payoneer Escrow too.) gave me some great feedback on my article to get it all nice and organized. I’ve taken a technical writing class, but I wouldn’t call myself a pro writer. I don’t have any experience in the writing field (Adam does) besides writing APA papers in college. Anyways, thank you, Adam, for the great feedback!

--

--