At my work I have to build solutions around communication between browser and iframe or a popup window .ie <iframe>
or window.open()
At my work I have to build solutions around communication between browser and iframe or a popup window .ie <iframe>
or window.open()
. I have to use postMessage
js api, and feel it is not very developer friendly. Consider the below situation
While this may seem trivial when you are doing simple you are doing simple http calls from frontend but across windows something like this is much more complicated. We have also to keep in mind that security is our outmost concern.
Let us try to code this thing. Parent is parent.com, child is at child.com.
I am assuming parent has child as an iframe
1link//parent.com
2linkiframe.contentWindow.postMessage(message, 'child.com');
3link
1link//child.com
2linkwindow.addEventListener('message', (event) => {
3link const expectedOrigin = 'parent.com';
4link if (event.origin !== expectedOrigin) {
5link return
6link }
7link const message = event.data;
8link doPorcessingOfMessage(message)
9link})
1link//child.com
2linkasync function doPorcessingOfMessage(message){
3link const resp = await api(message);
4link return resp;
5link}
1link//child.com
2linkwindow.addEventListener('message', (event) => {
3link const expectedOrigin = 'parent.com';
4link if (event.origin !== expectedOrigin) {
5link return
6link }
7link const message = event.data;
8link const resp = await doPorcessingOfMessage(message);
9link window.parent.postMessage(resp, 'parent.com');
10link})
11link
1link//parent.com
2linkwindow.addEventListener('message', (event) => {
3link const expectedOrigin = 'child.com';
4link if (event.origin !== expectedOrigin) {
5link return
6link }
7link const message = event.data;
8link
9link})
1link//parent.com
2linkwindow.addEventListener('message', (event) => {
3link const expectedOrigin = 'child.com';
4link if (event.origin !== expectedOrigin) {
5link return
6link }
7link const message = event.data;
8link const newMessage = getNewMessage(message);
9link iframe.contentWindow.postMessage(message, 'child.com');
10link})
As we can see the process is completely event based. And writing code to do something like this gets very messy.
What if there was nicer way to do these. Let us say
1link//parent.com
2linkconst resp1 = await sendMessageToChild(message1);
3linkconst message2 = await someProcessing(resp1);
4linkconst resp2 = await sendMessageToChild(message2);
1link//child.com
2linkconst message1 = await receiveMessageFromParent();
3linkconst resp1 = await someProcessing(message1);
4linkawait sendMessageToParent(resp1)
5link
In the above scenario, the parent is the orchestrator and child is just responding to actions triggered by parent.
In a different scenario a child can be the orchestrator and the parent would be just responding to actions triggered by the child.
That means both the parent and child can send and receiver. If parent is send then child has to be receive, and when parent is receiver the child has to be sender.
We have to make sure that there are retries in communication. And also both parent and child should be ready to receive.
We have to have origin checks in both child and domain. Ignore all messages if not received from where we want to receive
I think the best way to do this would be to build it like a client/server kind of a thing. Let us take an example of web client and node js express server.
1linkfetch('/message').then(response => response.json())
2link .then(data => {
3link document.getElementById('response').textContent = JSON.stringify(data, null, 2);
4link })
5link .catch(error => {
6link document.getElementById('response').textContent = 'Error: ' + error;
7link });
1linkapp.get('/message', (req, res) => {
2link res.send('Hello, world!');
3link});
I started to write the solution but soon realized it is not easy. So I have to take a more conservative approach
Ruto is a lightweight(4KB), fast and easy-to-use JS library that streamlines the communication between parent and child window(iframe/popup).
It uses client-server design pattern to communicate between parent and child window. Any window can become the client or the server depending on who wants to send. It abstracts out the complications of postMessage API and provides a simple API to send and receive messages.
You can visit the github page here
Parent will send a message iframe, Parent expects a response from the iframe with 10 secs
1link<iframe id="iframe" src="https://example.com/child.html"></iframe>
2link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
3link<script>
4linkconst iframe = document.getElementById('iframe');
5linkconst message = 'Hello from parent';
6linkconst options = {
7link timeout: 10000
8link}
9link
10linkruto.send("https://example.com/parent-to-iframe/topic1", iframe, message, options).then((response) => {
11link console.log(response); //Hello from child
12link}).catch((error) => {
13link console.log(error);
14link});
15link</script>
1link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
2link<script>
3linkruto.receive("/parent-to-iframe/topic1", window.parent, (response, message) => {
4link console.log(message); //Hello from parent
5link return response.send('Hello from child'); //ra
6link});
7link</script>
Iframe will send a message to parent, and expects a response from parent within 5 secs
1link<iframe id="iframe" src="https://example.com/child.html"></iframe>
2link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
3link<script>
4linkconst iframe = document.getElementById('iframe');
5linkruto.receive("/iframe-to-parent/topic1", iframe, (response, message) => {
6link console.log(message); //Hello from iframe
7link return response.send('Hello from parent');
8link});
9link</script>
1link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
2link<script>
3linkconst message = 'Hello from iframe';
4linkconst options = {
5link timeout: 5000
6link}
7linkruto.send("https://example.com/iframe-to-parent/topic1", window.parent, message, options).then((response) => {
8link console.log(response); //Hello from parent
9link}).catch((error) => {
10link console.log(error);
11link});
12link</script>
Parent will send a message to popup, and expects a response from popup within 5 secs
1link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
2link<script>
3linkconst popup = window.open('https://example.com/popup.html', 'popup', 'width=600,height=400');
4linkconst message = 'Hello from parent';
5linkconst options = {
6link timeout: 5000
7link}
8linkruto.send("https://example.com/parent-to-popup/topic1", popup, message, options).then((response) => {
9link console.log(response); //Hello from popup
10link}).catch((error) => {
11link console.log(error);
12link});
13link</script>
1link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
2link<script>
3linkruto.receive("/parent-to-popup/topic1", window.opener, (response, message) => {
4link console.log(message); //Hello from parent
5link return response.send('Hello from popup');
6link});
7link</script>
Popup will send a message to parent, and expects a response from parent within 5 secs
1link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
2link<script>
3linkconst popup = window.open('https://example.com/popup.html', 'popup', 'width=600,height=400');
4linkruto.receive("/popup-to-parent/topic1", popup, (response, message) => {
5link console.log(message); //Hello from popup
6link return response.send('Hello from parent');
7link});
8link</script>
1link<script src="https://cdn.jsdelivr.net/gh/rajnandan1/ruto/dist/ruto.min.js"></script>
2link<script>
3linkconst message = 'Hello from popup';
4linkconst options = {
5link timeout: 5000
6link}
7linkruto.send("https://example.com/popup-to-parent/topic1", window.opener, message, options).then((response) => {
8link console.log(response); //Hello from parent
9link}).catch((error) => {
10link console.log(error);
11link});
12link</script>