Using postMessage to implement an inter-frame API

One of the products I work on allows users to host individual web pages inside frames within it. Whilst working on a proof-of-concept a while ago, I stumbled acros the postMessage method which enables cross-origin communication across frames / windows. This led me to implement a simple example (which I used in a stackoverflow answer today), although ultimately the proof-of-concept didn't go anywhere and we didn't progress the concept into production.

In the outer page (the one hosting the IFrame(s)) there needs to be code that calls postMessage, for example:

function callFrameFunction(functionName, parameters)
{
    /// <summary>Call a function against all frames</summary>
    /// <param name='functionName'>The name of the function to be invoked in the child page</param>
    /// <param name='parameters'>The parameters to pass to the function in the child page</param>
    var frames = document.getElementsByTagName('iframe');
    for (var i = 0; i < frames.length; i++)
    {
        try
        {
            var frame = frames[i];
            var message =
                {
                    Function: function,
                    Parameters : parameters
                };
            frame.contentWindow.postMessage(message, "*");
        }
        catch (e)
        {
            // Code to handle errors would go here!
        }
    }
}

This method would be called, in a manner such as:

callFrameFunction('AFunctionInTheFrame', { param1: 'Value', param2: 3, param3: false });

There wouldn't necessarily be a function named AFunctionInTheFrame inside the IFrame, however what there would be is code that responds to the "message" event being raised by un-packing the information sent over and then performing the work required (for example, calling a function called "AFunctionInTheFrame" that has three parameters called param1, param2 and param3). An example of this, albeit that only alerts out the name of the function invoked would be:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
    // logic that reacts to the event/message goes here
    switch(event.data.FunctionName)
    {
        case "AFunctionInTheFrame":
            alert('Function called was: AFunctionInTheFrame');
            break;
        case "AnotherFunctionInTheFrame":
            alert('Function called was: AnotherFunctionInTheFrame');
            break;
    }
}

Yes, if the outer page and the inner page (in the IFrame) are from the same origin, you can call JavaScript methods directly - but this does mean you can't (a) Move them to separate origins, and (b) Change the functions (i.e. their names or signatures) in the IFrame hosted page without also changing the outer page. Using postMessage covers both issues, particularly the latter as any change to functions defined in the IFrame hosted page only require changes to be made inside the function that handles the message from postMessage being received


No Comments

Add a Comment