Friday 22 May 2015

Using IFrame and postMessage to perform cross page communication

This is a JavaScript function which wraps all the necessary logic to communicate with a target page, below are the key points of this implementation:
  1. target page is loaded using an hidden iframe
  2. once iframe finishes loading, send data over via postMessage
  3. register a function to wait for and respond to message to be received from target page only once
  4. async operation has been wrapped with a jQuery promise
function sendAndReceiveMessage(targetUrl, targetOrigin, dataToSend){
 //Utility routine to generate a unique id
 function createGuid()
 {
     return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
         var r = Math.random()*16|0, v = c === 'x' ? r : (r&0x3|0x8);
         return v.toString(16);
     });
 }

 var deferred = $.Deferred();

 var iframeID = createGuid();
 var iframe = $("<iframe></iframe>", {
           "src": targetUrl,
           "id": iframeID,
           "style": "display:none;"
          });
 $("body").append(iframe);
 
 iframe.one("load", function(){
  var childFrame =  $("#"+iframeID)[0];
  childFrame.contentWindow.postMessage(JSON.stringify(dataToSend), targetOrigin);
 });

 $(window).on("message", handleMessage);

 function handleMessage(event){
  $(window).off("message", handleMessage);
  var dataReceived = JSON.parse(event.originalEvent.data);
  $("#"+iframeID).remove();
  deferred.resolve(dataReceived);
 }

 return deferred.promise();
}


 
Usage example:

  $("#btnTest").click(function(){
   var dataSend = {"value": 123};
   sendAndReceiveMessage("child.html", "*", dataSend).done(function(data){
    alert("Test result: " +data.value);
   });
  });


A sample implementation of child.html is below. In this case I just double the value received from calling page and return it back.


(function(){
 function receiveMessage(event){
  var data = JSON.parse(event.data);
  data.value *= 2;
  event.source.postMessage(JSON.stringify(data), "*");
 }

 $(document).ready(function(){
  window.addEventListener("message", receiveMessage, false);        
 });

})();