Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Damien Cosset
Damien Cosset

Posted on

     

Iframes and communicating between applications

Introduction

Iframes are awesome! They allow you to embed another HTML page inside the current one. The embedded page carries its own browsing context with it. So, if a HTML page creates an iframe with a remote application as its source, you'll have the first applicationhosting that remote application with all its functionalities. It's a technique that's used by a lot of companies to allow developers to use their service easily ( Stripe and Yousign come to mind)

The problem

The problem is this: I want an iframe to be added to the HTML DOM when the user interacts with an element, in my case, a click on a button. I want that iframe to take up the entire page. From the user perspective, it would look like you actually travelled to a new page, or that a full width modal just opened.

Setting up

So, we need an 2 applications. One of them, when we click on a button, will open an iframe. Inside that iframe will be embedded the second application. I'll use React for both my applications, but the concepts work with any framework.

Let's create our two React application. I'll do that withcreate-react-app. So, I'll runcreate-react-app main-app andcreate-react-app iframe-app.

Go the to theApp.js file inside themain-app React application and add a button to open an iframe:

importReactfrom"react";import"./App.css";functionApp(){letopenFrame=()=>{letiframe=document.createElement("iframe");iframe.src=`http://localhost:3001`;iframe.frameBorder="0";iframe.id="iframe";iframe.style.position="absolute";iframe.style.zIndex="999";iframe.style.height="100%";iframe.style.width="100%";iframe.style.top="0";iframe.style.backgroundColor="white";iframe.style.border="none";document.body.prepend(iframe);document.body.style.overflow="hidden";};return(<divclassName="App"><headerclassName="App-header"><p>Thisappopensaniframeandrunsonport3000</p><buttononClick={()=>openFrame()}>OpenIFRAME</button></header></div>);}exportdefaultApp;
Enter fullscreen modeExit fullscreen mode

So, this application runs on port 3000 and open an iframe when the user clicks on the button. That will create an iframe with thesrc attributehttp://localhost:3001 where our second application will run.

Notice that I wrote that in vanilla javascript to show you how it could be used anywhere.

Then, we are adding some styles to make our iframe take up the whole page, just like if it was a different page. Notice that we also setoverflow: hidden on the body, to not be able to scroll the main page.

Now, go to the second application iniframe-app and change theApp.js file:

importReactfrom"react";import"./App.css";functionApp(){letcloseIframe=()=>{};return(<divclassName="App"><buttononClick={()=>closeIframe()}>CloseIframe</button><p>Thisapprunsonport3001andhisembeddedinsidetheiframe</p></div>);}exportdefaultApp;
Enter fullscreen modeExit fullscreen mode

This application will run on port 3001. When we click on the button, we will close the iframe.

Make sure your main application is running on port 3000, and your iframe application is running on port 3001. (by runningPORT=3001 yarn start)

Ok, so if you now go tohttp://localhost:3000 in your browser, and click on theOpen IFRAME button. You will see the second React application take up the whole page inside its iframe. We're still on the port 3000 page. From the user, it doesn't look like an iframe at all though!

Before opening the iframe

After opening the iframe

Awesome, now, our first app correctly opens an iframe. The functionality works as expected.

Closing the iframe

Now, what we need to do next is allow the user to close the iframe. Since we want the user to experience our iframe opening as a modal or a new page, we need to give him a way to close/go back.

It does seem easy. Add a close button, click on it, then make the iframe disappear. Well, it's not that simple. The React application is on a different domain from the HTML page. The functionality to close the iframe will start on the React application. But we will try to manipulate the DOM of the first application. For security reasons, we can't manipulate the DOM from another domain (thankfully...). There are two ways we can solve this issue:

  • Make the React applications communicate with one another.
  • Create a header that would still be part of the first React application.

The second solution is the simplest one. Just style your DOM to show a button above the iframe content (maybe using some z-index styles), or show a header above the iframe (so the iframe would not take the whole height of the page, leaving some space for that header).

The second solution, for our purposes, doesn't suit me. So, to make both pages communicate with one another, we will usewindow.postMessage()

ThepostMessage function allows to send messages between cross-origin domains. When we would want to close our iframe, we will use this function to tell the main HTML page that we need to make the iframe disappear.

Adding the closing functionality

MDN PostMessage definition

We need to callpostMessage on thetargetWindow. The target window, in our case, is the window of the HTML page. We can get that window's reference withwindow.parent. Note that in the main HTML page, which does not have a parent,window.parent is the main window.

The first argument that the postMessage function takes is a message. You could send an object if you wish, or a string. Here, we don't need to send anything special, so I'll just call itclose-iframe. The second argument it takes is the url of the target window. That would behttp://localhost:3000 in our case. But, we want to make that dynamic:

letcloseIframe=()=>{leturl=window.location!=window.parent.location?document.referrer:document.location.href;window.parent.postMessage("close-iframe",url);};
Enter fullscreen modeExit fullscreen mode

Notice how we retrieve the parent's url. If the window's location is different from the parent's location, we'll get it throughdocument.referrer, otherwise, for IE browsers, we'll get it with document.location.href.

Get the message in the main application

Now that the iframe application sends a message, we need the main application to catch it. To do that, we can use theaddEventListener method. We will add this event listener inside auseEffect hook.

// Inside your App.js fileuseEffect(()=>{window.addEventListener("message",function(event){letframeToRemove=document.getElementById("iframe");if(frameToRemove){frameToRemove.parentNode.removeChild(frameToRemove);document.body.style.overflow="inherit";}});});
Enter fullscreen modeExit fullscreen mode

ThepostMessage function sends amessage event. Inside thisaddEventListener, we retrieve our iframe element and we remove it from the DOM. This is how it will looks like in your browser.

Congratulations! You can now make two applications communicate with one another through an iframe. Now, remember that the postMessage can work both ways. We made it from from child to parent, but parent to child is also valid!

Have fun ❤️

Top comments(6)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
wobsoriano profile image
Robert
[object Object]
  • Location
    CA
  • Joined
• Edited on• Edited

Bookmarked! I've been using this scheme to allow cross domain communication between two or more websites using a central SSO domain.

I think this is the general schema used by google and other social app sites for their SSO implementation. If you browse the gmail or youtube code you will see many things and other additional fields. Google also add an origin restriction. If you want to use the accounts.google.com SSO you have to register in google apps, get an integration ID and specify your authorized origins.

You can also do origin check in your main application by checking if the main app domain is equal to theevent.origin received from apostMessage

constsourceDomain='your-website-domain';addEventListener("message",_listener,false);function_listener(event){//origin checkif(sourceDomain.lastIndexOf(event.origin)==-1){return;}// do something}
CollapseExpand
 
asimen1 profile image
Asaf
  • Joined

Cool article! A couple of issues though:

  1. Using document.referrer inside the frame might pose a security risk since the frame can be embedded in an external site and hijack communication. It's best to be explicit with the allowed domains.

  2. Don't forget to include the dependencies array of the useEffect hook otherwise you might run into issues... see here:betterprogramming.pub/stop-lying-t...

Btw I wrote an open source library to simplify communication between frames - it’s called iFramily (github.com/EkoLabs/iframily). Basically it has a simpler API than postMessage, which includes Promise-based responses, message queuing, and managing the connection until both frames are ready to talk. It also takes a responsible approach to security...

CollapseExpand
 
livingstonex profile image
livingstonex
  • Joined

Does this work when your react app is being opened from another domain entirely?
For example, your app is hosted on x.com, and is being opened in an iframe, from an app that is hosted on y.com, does this cross-domain message posting work in that scenario?

CollapseExpand
 
andy profile image
Andy Zhao (he/him)
uh oh where'd my bio go!
  • Education
    Actualize Coding Bootcamp
  • Joined

Very cool! Neat implemention and problem solving!

Also cc@ben who loves iframes

CollapseExpand
 
damcosset profile image
Damien Cosset
French web developer mostly interested in Javascript and JAVA

Took me a while to figure out how I could actually solve that problem! Now here for all, for eternity!!!

CollapseExpand
 
jwp profile image
JWP
WebCompnents and AI
  • Location
    Minneapolis, MN
  • Joined
• Edited on• Edited

How was Cors not an issue between port 3000 and 3001?

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

French web developer mostly interested in Javascript and JAVA
  • Location
    France
  • Joined

More fromDamien Cosset

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp