SHARE
Facebook X Pinterest WhatsApp

Display Cross-domain Data Using postMessage()

Written By
thumbnail
Rob Gravelle
Rob Gravelle
Dec 4, 2017

Perhaps there is no greater bane to a web developer’s existence than the same-origin policy. In the Fetch Cross-domain Content Using a PHP Proxy article, I presented one way to serve web content from another domain. Now I’d like to share a relatively new technique that utilizes the JavaScript postMessage() method.

Why Browsers Limit Data Sharing Between Web Pages

The same-origin policy is part of the web application security model. It basically permits scripts running on pages originating from the same site to access each other’s data, but prevents scripts from accessing data that is served from a different domain. The idea is to prevent a malicious script on one page from obtaining access to sensitive data on another through that page’s Document Object Model (DOM).

To illustrate, let’s say that site one has a JS script in it. It has pretty much free reign over its own page content as well as the ability to fetch more content from its own server. However, should it try to fetch a resource from another server, it will fail with the following error message:

XMLHttpRequest cannot load http://www.domain.com/path/filename. Origin null is not allowed by Access-Control-Allow-Origin.

same-origin (38K)

Enter the postMessage() Method

PostMessage() is a global method that safely enables cross-origin communication. It’s a lot like Ajax but with cross-domain capability. We’ll give it a whirl by setting up two-way communication between a web page and an iframe whose content resides on another server. In order to avoid using multiple servers or hosting one of the pages on my own site, we’ll cheat a little bit by viewing the parent locally and hosting the iframe page on a development server. You can use any server you like because the iframe only requires one .html file. I personally use WampServer.

The Main Page

Our parent page includes an iframe that contains the hosted postMessageReceiver.html page, along with a form for sending messages to the iframe:

<style>
body {
  background-color: lightgray;
}

iframe {
  display:block;
  width:500px;
  height:300px;
  margin-top: 5px;
  border: medium solid black;
  background-color: white;
}
</style>
<h1>postMessage() Demo</h1>

<div class="container-fluid">
<p>
  <form id="form">
    <input type="text" id="msg" placeholder="Type message to send"/>
    <input type="submit" value="Send message" />
  </form>
  <p id="msg"> </p>
  <iframe id="externalContent" src="http://localhost:8080/postMessage/postMessageReceiver.html"></iframe>
</p>
</div>

There is also a script that handles the sending and receiving of messages.

On the form submit, we need to reference the iframe’s content window and invoke its global postMessage() method, passing in the JSON-encoded message and the iframe’s origin. Note that the message does not have to be JSON; any string is fair game:

//expected origin of iFrame content...
var ORIGIN = "http://localhost:8080";

//send a message into the hosted iFrame...
document.getElementById("form").onsubmit = function(e){
  //target the iFrame
  var win = document.getElementById("externalContent").contentWindow;

  //use JSON.stringify() to send text...
  win.postMessage(JSON.stringify({ 'newMessage': document.getElementById("msg").value }), ORIGIN);

  return false;
};

There’s also a function for accepting incoming messages from the iframe. Here, we have a check against the expected sender’s origin so that we may ignore anything that comes in from an unexpected source:

origin_check (45K)

The message is then displayed in a paragraph element:

//listen for messages coming from the expected origin...
function listener(event){
  if(event.origin !== ORIGIN){
    return;
  }

  var response   = JSON.parse(event.data),
      newMessage = response['newMessage'];

  document.getElementById("msg").innerHTML = "received: \""+(newMessage ? newMessage : 'nada')+"\" from: "+event.origin;
}

The iframe Content

The iframe page – named postMessageReceiver.html – is hosted on the server. To make that apparent, there is a dynamically populated field that displays the domain.

This page also contains a form for sending data to the parent page:

<p><b>This iframe is located on <span id="domain"> </span>.</p>

<div id="msg"> </div>

<form id="form">
    <input type="text" id="msg" placeholder="Type message to send"/>
    <input id="send_msg" type="button" value="Send message to parent page" />
</form>

You’ll notice that the JS code here is much the same as the parent’s except for the code that displays the domain as well as one other thing which we’ll get to in a moment:

var ORIGIN = 'file:';

document.getElementById('domain').innerText = location.protocol + '//' + location.host;

//these are functions that are called from the parent page into this one...
function listener(event){
  if(event.origin !== ORIGIN){
    return;
  }
  console.log(event);

  var response   = JSON.parse(event.data),
      newMessage = response['newMessage'],
      origin     = event.origin == 'file:' ? 'local parent page' : event.origin;
  
  document.getElementById("msg").innerHTML = "received: \""+(newMessage ? newMessage : 'nada')+"\" from: "+origin+".";
}

The other addition is in the code that sends messages to the parent page. If you look closely at the postMessage() call, an origin value of “*” is passed for the local page. That’s done because the “file:” protocol is not permitted. The asterisk means that we don’t care who the targetOrigin is:

//send a message to the parent page...
document.getElementById('send_msg').addEventListener('click', function(e) {
  window.parent.postMessage(JSON.stringify({ 'newMessage': document.getElementById("msg").value }), 
                           (ORIGIN == 'file:' ? '*' : ORIGIN));
}, false);

Here’s the demo in action:

demo (44K)

Speaking of which, here is a .zip archive with the two pages. Just remember to put the postMessageReceiver.html file on your server and access postMessageDemo.html directly from your file system – i.e. “C:\postMessage\postMessageDemo.html”.

Conclusion

You’ll be happy to know that the window.postMessage API enjoys support in all of the most popular modern browsers, with only IE 11 providing partial support. Having said that, I ran my demo on it and had no issues.

Recommended for you...

Best VR Game Development Platforms
Enrique Corrales
Jul 21, 2022
Best Online Courses to Learn HTML
Ronnie Payne
Jul 7, 2022
Working with HTML Images
Octavia Anghel
Jun 30, 2022
Web 3.0 and the Future Of Web Development
Rob Gravelle
Jun 23, 2022
HTML Goodies Logo

The original home of HTML tutorials. HTMLGoodies is a website dedicated to publishing tutorials that cover every aspect of being a web developer. We cover programming and web development tutorials on languages and technologies such as HTML, JavaScript, and CSS. In addition, our articles cover web frameworks like Angular and React.JS, as well as popular Content Management Systems (CMS) that include WordPress, Drupal, and Joomla. Website development platforms like Shopify, Squarespace, and Wix are also featured. Topics related to solid web design and Internet Marketing also find a home on HTMLGoodies, as we discuss UX/UI Design, Search Engine Optimization (SEO), and web dev best practices.

Property of TechnologyAdvice. © 2025 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.