Quite common situation is that we place part of the content of our site in IFRAME tag which is linked with another page, typically, in the same domain as our main page. This is a very common idea when we are building a bigger application which is build of micro-front-ends (micro-front-end is an architecture pattern similar to micro-services in back-end applications). Each micro-front-end is a separate smaller app. We can combine by using IFRAME many micro-front-ends apps into one bigger fully functioning application.
Table of Contents
How do iframe and parent site communicate?
In all above cases the main question is – how parent page communicates with child page in IFRAME html tag? You may wonder if an iframe or new tab window can communicate with its parent window? Of course, yes, it is! And I will show in this article how to do that in a correct way 😊 Of course with JavaScript 😊
For bidirectional communication we will use two native JavaScript functionalities:
- postMessage method
- addEventListnere for “message” event
Let’s have a closer look at both of them.
What is JavaScript window postMessage?
Window.postMessage is the native JavaScript method. Native means that will work directly under browser without any other tool between your code and website (like babel for transpilation) and it makes easy communication between two or more front-ends (apps, webpages etc) layers running under separated windows which are related to each other (parent-child relation)
When should I use in JavaScript Windows postMessage?
JavaScript window postMessage method makes possible cross-origin communication between windows. You should use it when you want to send data back and forth between two web pages or webapps running under different locations (urls) when one window (page) is located inside another page or is opened from it.
Situations when to use postMessage for sending data in details:
- new webpage (child) is opened in new window (popup) from parent webpage window
- new webpage (child) is opened in new tab window from parent webpage window
- another webpage (child) is opened in iframe window embedded in parent webpage window
- communication between many micro-front-ends apps (running inside many iframe windows) and its parent (hosting) app
Key here is “cross-origin” communication – it means that we can communicate by window.postMessage with pages (websites, webapps, micro-front-end apps) hosted in another server.
It means that this is a great solution when one (child) application in one window must be embedded in iframe tag of parent app (window) and when both apps must exchange data between each other! Then both, child and parent window emit postMessage event to send needed data. The same postMessage event of data exchange can be emitted between two windows (when new window is opened from first (parent) window).
But there are some important things to remember when building communication like that, check examples below to see how to do it well.
Javascript window postMessage example
I described below a few key examples how to use postMessage functionality. First example shows what is the generic concept under that. Next examples shows how to make fully working solution between parent window and child windows in iframe or new tab / window.
Generic example of JavaScript postMessage
In the easiest way, the steps how to use that are quite easy. But be careful! This is the easiest example of postMessage which will work without problems in pure html/JavaScript webpages. But when you need solution for SPA app (vanilla js/react/vuejs) or complex website build on top of some framework (like WordPress) you may find problems with using this direct approach. Read my article further to learn more. Here you have the example of the basics:
- Find window to which you want send data via postMessage.
– This can be child window like: iframe, new tab window.
– Or a parent window – the same, partent for iframe embedded window or window opener. It looks as following:
Finding a child window:
a) Iframe:
var childWindow = document.getElementById("your_iframe_tag_id");
childWindow = frame ? frame.contentWindow : null;
b) Newly opened window/tab:
var childWindow = window.open("frame.html");
Finding a parent window for child window in:
a) Iframe:
var parentWindow = window.parent;
b) In new tab / new window:
var parentWindow = window.opener;
This is very important step! Because we look how we find the child or parent window in JavaScript code is tightly connected to way how this window was created.
- Second step is piece of cake, in easiest variant just code:
To post message from PARENT to CHILD window:
childWindow.postMessage(“Your message. Text, number, object, whatever.”, "*");
And to post message from CHILD to PARENT window:
parentWindow.postMessage(“Your message. Text, number, object, whatever.”, "*");
So simply – take variable with child or parent window from step 1 above and use postMessage method. This method takes the following attributes:
- Message – this can be whatever you want, all JavaScript valid variables or data types can be sent here. But there is a good pattern how to send messages. Read this article further to learn how to o do it well.
- “*” – targetOrigin. Here in example wildcard *. But this is not good to put here wildcard * mark, best case is to put here the target URL where you send the data.
- Last step is to receive message send from parent. Just put eventListener “message” on own window object. No matter if this is parent or child window:
window.addEventListener("message", (e) => { var data= e.data; // hare are data sent by other window postMessage method });
If you do everything correctly but something still is not working than read advanced example below.
Advanced example of JavaScript postMessage
OK so in above example you have learnt how to send and receive data between child and parent window, no matter how it is embed (iframe) or open (new tab). But did you think what is wrong with that approach from basic example above?
The biggest problem is that you never know when child or parent window page will be ready to receive data! Why?
Because when new tab will be opened, or new page will be triggered in iframe window, it takes some time when new page will load and JavaScript code with eventListener for massages will be mounted from child window page JavaScript code.
If you send data to early, when child page is not fully loaded, than it will not receive data. Simple like that.
In comparison to real life, this is a situation when you would like to send a postcard to your child, before he or she mounted post box in front of his or her new home. There will be postal address to send postcard, but there will be no mounted place to receive that.
So, like in real life, your child must first inform you that his or her home has postbox, and you can send a postcard with message 🙂
How do I know if an iframe is loaded?
I name that functionality the “windows hand-shake”. Ide idea is easy – CHILD window must inform a PARENT window when it is loaded, then PARENT can send data to CHILD. Step by step:
- When CHILD window is loaded, then you must mount to “load” event a function which will notify parent window (if exists) that is now fully loaded and ready to receive data from parent.
- Parent must listen CHILD window for shake-hand message.
- When shake-hand message is received from CHILD, than PARENT window can send data to CHILD or proceed any other communication schema you want to implement.
Let’s create now example how to make a proper communication between tabs and/or windows in Javascript with “shake-hand” usage. Check the code below, its screen shot with explanation and running example:
WORKING EXAMPLE OPEN HERE (remeber to open console window in dev tools – press F12 there) or check below:
CODE SCREEN SHOT WITH STEP BY STEP DESCRIPTION:
PARENT WINDOW CODE:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="expires" content="0"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> </head> <body> <h1>CHILD</h1> <div> <h3>Received data:</h3> <div id="placeholder"></div> </div> <script> /* DEFINITIONS */ var parentWindow = window.parent; /* * RECEIVE MESSAGE FROM PARENT */ window.addEventListener("message", (e) => { var data = e.data; console.log("RECEIVED message from PARENT TO CHILD", data); document.getElementById("placeholder").innerText = data; }); /* SHAKE HAND WITH PARENT */ window.addEventListener("load", () => { parentWindow.postMessage("shakehand", "*"); }); </script> </body> </html>
CHILD WINDOW CODE:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="expires" content="0"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <style> iframe { max-width: 100%; width: 800px; height: 300px; } </style> </head> <body> <h1>PARENT WINDOW</h1> <h2>Automatic data send from parent to child when child window is fully loaded</h2> <iframe id="mainframe" src="child.html"></iframe> <script> /* DEFINITIONS */ var frame = document.getElementById("mainframe"); frame = frame ? frame.contentWindow : null; /* * RECEIVE MESSAGE FROM CHILD */ window.addEventListener("message", (e) => { var data = e.data; if(data === "shakehand") { console.log("SHAKEHAND RECEIVED FROM CHILD") frame.postMessage("HARE AND NOW THIS TEXT IS BEING SENT TO CHILD", "*"); } }); </script> </body> </html>
Fully advanced example of JavaScript postMessage
I created for you as addition fully advanced example of JavaScript postMessage functionality. This is much more complex communication schema, but still based on windows “shake-hand” idea. In comparsion to previous example, you can find here:
- First action – automatic postMessage to CHILD window – the same like in previous example (I added here setTimeout to simulate longer loading of child window)
- Second action – postMessage to CHILD window triggered manually by button click.
- Third action – PARENT receives message from CHILD window triggered by manually by button click.
Important code parts from that example are:
- I implanted here a dedicated MESSAGE class which allows you to create messages in the same way, where always the TYPE of the messages is set. TYPE is like an endpoint in API, it tells CHILD or PARENT window what action you want to trigger and what to do with received data:
class Message { constructor(type, body) { this.type = type; this.body = body; } }
- sendMessage method, which takes as argument the window object (can be CHILD or PARENT window reference object), and PAYLOAD, so data to be sent:
function sendMessage (windowObj, payload) { if(windowObj) { windowObj.postMessage(payload, "*"); } }
- Next in both CHILD and PARENT windows in
window.addEventListener("message", () => {});
methods are in use TYPEs which identifies actions to be taken on certain request (check on screen shot below)
This example shows full capabilities and power of data exchange between 2 front-end application when one is embed or opened from another one.
Check the code below, its screen shot with explanation and running example:
WORKING EXAMPLE OPEN HERE (remeber to open console window in dev tools – press F12 there) or check below:
PARENT WINDOW CODE:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="expires" content="0"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <style> iframe { max-width: 100%; width: 800px; height: 300px; } </style> </head> <body> <h1>PARENT WINDOW</h1> <h2>Send data to child</h2> <ul> <li>1st action - automatic data send to child when it's loaded<br> - Status: <strong id="status-1">...WAITING...</strong></li> <li> 2nd action - trigger data send by button: <button id="btn">Submit</button><br> - Status: <strong id="status-2">...NOT STARTED...</strong> </li> <li> 3rd action - parent to receive message from child. Click button "Send message to PARENT" in child frame.<br> - Status: <strong id="status-3">...NOT STARTED...</strong> </li> </ul> <iframe id="mainframe" src="child.html"></iframe> <script> /* DEFINITIONS */ var btn = document.getElementById("btn"); var frame = document.getElementById("mainframe"); frame = frame ? frame.contentWindow : null; class Message { constructor(type, body) { this.type = type; this.body = body; } } function sendMessage (windowObj, payload) { if(windowObj) { windowObj.postMessage(payload, "*"); } } /* * RECEIVE MESSAGE FROM CHILD */ window.addEventListener("message", (e) => { var data = e.data; console.log("RECEIVED message from CHILD TO PARENT", data); var type = data.type; var body = data.body; if(type === "shakehand" && body) { console.log("SHAKEHAND RECEIVED FROM CHILD, SENDING dummyDataToSend TO CHILD...") sendMessage(frame, new Message("data", dummyDataToSend)); document.getElementById("status-1").innerText = "DONE"; } else if (type === "text-msg" && body) { console.log("TEXT MESSAGE RECEIVED FROM CHILD"); document.getElementById("status-3").innerText = "DONE"; alert("TEXT FROM CHILD WINDOW: " + body); } }); /* * SEND MESSAGE TO CHILD */ var dummyDataToSend=[{name:"Monitor",price:"400",currency:"USD",description:"The best monitor made in USA."},{name:"CPU",price:"70",currency:"EUR",description:"The fastest CPU."},{name:"GPU",price:"500",currency:"USD",description:"WOW, so fast GPU for gaming"},{name:"Keyboard",price:"50",currency:"EUR",description:"Ergonomic keyboard for geeks"}]; btn.addEventListener("click", () => { sendMessage(frame, new Message("data", dummyDataToSend)); document.getElementById("status-2").innerText = "DONE"; }); </script> </body> </html>
CHILD WINDOW CODE:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="expires" content="0"> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> </head> <body> <h1>CHILD</h1> <div> <button id="btn">Send message to PARENT</button> <h3>Received data:</h3> <div id="placeholder"></div> </div> <script> /* DEFINITIONS */ var parentWindow = window.parent; var btn = document.getElementById("btn"); class Message { constructor(type, body) { this.type = type; this.body = body; } } function sendMessage (windowObj, payload) { if(windowObj) { windowObj.postMessage(payload, "*"); } } /* * RECEIVE MESSAGE FROM PARENT */ window.addEventListener("message", (e) => { var data = e.data; console.log("RECEIVED message from PARENT TO CHILD", data); var type = data.type; var body = data.body; if(type === 'data') { console.log() document.getElementById("placeholder").innerText += JSON.stringify(body); } }); /* SHAKE HAND WITH PARENT */ window.addEventListener("load", () => { setTimeout(function () { sendMessage(parentWindow, new Message("shakehand", true)); }, 2000); }); /* * SEND MESSAGES TO PARENT */ btn.addEventListener("click", () => { sendMessage(parentWindow, new Message("text-msg", "Dummy message, just button was clicked!")); }); </script> </body> </html>
React, Vue or Angular iframe postMessage communication
The same rule you must follow when you use JavaScript frameworks like Vue, React, Angular or any other framwework. But remember! When using frameworks you can’t mount to normal window “load” event listener. You must use LIFECYCLE HOOKS of certain framework. Like:
mounted
in VueJS.componentDidMount
in ReactJSngAfterViewInit
in AngularJS
Summarize
This is the end of this article. I hope I explained in details how the proper communication schema between two windows, so also two front-end application when one is embed or is opened in a new tab from another application. This situation happens often in some integrations when smaller app should be available in a bigger one or when implementing the MICRO FRONT-ENDS pattern. Have fun 🙂