How to establish two way communication with iframe

This post is part of the series documenting a qualtrics project.

Right now we have a single page embedded inside a qualtrics survey, and we want to communicate with the iframe, that contains static HTML file.

One way communication

For one way communication you can use #fragment part of URL, that is the part after #. See the Wikipedia article on URLs.

The fragment part of URL is not sent to the server and is handled entirely client-side. You can access it using window.location.hash variable.

I did not implement communication this way, so I'll leave it to you to implement the details.

Two way communication

I needed two-way communication between the Qualtrics page (the page that embeds the iframe) and the iframe itself. Communication between hosting page and the iframe is quite restricted, especially if both pages have different origins.

To communicate between the frames you'll need to use window.postMessage API, this API allows you to safely send messages between frames even if origin mismatches.

Let's assume that the hosting page has the following iframe declaration:

<iframe id="studyFrame" src="https://somemeUrl">
</iframe>

Hosting page communication part

To send messages from the hosting page to the iframe, you'll need to:

var targetOrigin = "*";
var frame = window.frames['studyFrame'].contentWindow;
frame.postMessage({"message": "hello"}, targetOrigin);

To receive messages from the iframe you'll need to:

 function receiveMessage(event) {
   // if (event.origin !== "http://example.org:8080")
   //   return;
 }
window.addEventListener("message", receiveMessage);

Hosting page communication part

To receive messages inside the iframe you'll need to:

function receiveMessage(event) {
  // if (event.origin !== "http://example.org:8080")
  //   return;
  if (event.data['message'] === "hello"){
       // Do stuff
   }
}

window.addEventListener("message", receiveMessage);

To send messages from the iframe you'll need to:

var targetOrigin = "*";
window.parent.postMessage({
  "type": "finished",
  "success": success,
  "data": data,
}, targetOrigin)

On inter-frame communication security

In the examples above I have omitted some important security details, notably, you should:

  • send proper targetOrigin, this allows the browser to block sending the message, if for some (presumably evil) actor updated frame URL.
  • verify event.origin when receiving the message, this will allow you to discard messages sent by malicious actors.