Deep linking into an iframe, cross-domain

Why would you want to do this? My use case was a gallery application where we needed to deep-link to a specific gallery entry. Alas, the gallery would be iframed. Yes, iframes should be avoided, but sometimes in real-life you have to just deal with it.

Imagine my surprise when I discovered that passing the parent window.location object into an iframe across domains is not only possible, but is easy and works in most browsers, down to IE8.

The Solution

Use window.postMessage() to pass messages to the iframed window.

Outer page

window.onload = function()
{
    var child = document.getElementById('deep_link_frame');
    var msg   = {
        "location" : {
            "hash"     : window.location.hash,
            "host"     : window.location.host,
            "hostname" : window.location.hostname,
            "href"     : window.location.href,
            "origin"   : window.location.origin,
            "pathname" : window.location.pathname,
            "port"     : window.location.port,
            "protocol" : window.location.protocol,
            "search"   : window.location.search
        }
    };
    child.contentWindow.postMessage(JSON.stringify(msg), '*');
};

Inner page

function bindEvent(el, eventName, eventHandler)
{
    if (el.addEventListener)
    {
        el.addEventListener(eventName, eventHandler);
    }
    else
    {
        el.attachEvent('on' + eventName, eventHandler);
    }
}

bindEvent(window, 'message', function(e)
{
    if (e.origin === "http://your-domain.com")
    {
        var message = JSON.parse(e.data);
        alert(message.location.href);
    }
});

Limitations

There are a few land mines to watch out for if you need to support IE8 and IE9:

  • All messages should be sent as strings to avoid a nasty bug in IE8-9. If you want to pass an object, pass it in JSON format.
  • You can’t JSON.serialize() the window.location object in IE8. If you are trying to pass that object, you have to copy the properties one by one.
  • IE only supports el.contentWindow.postMessage(), not el.postMessage().
Written on October 22, 2013