Cross-domain AJAX

First of all, I am using the term AJAX loosely here.  It doesn't have to be XML or asynchronous. What matters is having Javascript code on the client communicating with a server and in this case with a server in a different domain from where the Javascript was loaded.

The problem:

Typically one uses XMLHttpRequest to communicate with an HTTP server from Javascript.  However, for security/privacy reasons, XMLHttpRequest does not allow one to request URLs in a different domain.  Making the problem worse, for a browser, http://am.net, http://www.am.net, and https://www.am.net are all different domains.

If you only need to send data to the server, this is easily done by submitting a form (with form.submit()).  To prevent the browser from leaving the current page and loading the server's response, either have the server respond with 204 No Content (in AOLserver this is as easy as [ns_return 204 "" ""]) or set the form's target to a hidden IFRAME.

But what if you need to get the server's response?  Although you can load the server's response into a hidden IFRAME, for security reasons, the parent window is unable to read anything from the document in the IFRAME, nor can the code in the IFRAME set anything in the parent window (or read from it).

The solution:

There is just one exception - although a window cannot READ window.location of another window in a different domain, it can SET it.  Normally, this isn't very helpful because setting location causes the browser to load that URL.  However, if you only change the part of the URL after # (i.e. the anchor) to a non-existent anchor, the browser will do nothing (except possibly make a clicking sound).

So, by setting strings in the anchor (known as hash in Javascript), our windows can communicate with each other.  This sounds like an incredible hack and while I agree, this is currently the ONLY way to do client-side cross-domain communication.

The issues:
  1. While the parent window can set the hash in a child frame/window directly, the child frame/window cannot set just the hash property of the parent.  It can only set the entire URL.  This is not trivial because we cannot READ the parent window's URL.

  2. In IE7, we cannot set the URL of the parent window if that window is itself an IFRAME in some parent window.

  3. The hash string has a limited length, 4000 characters in IE.

Dojo 0.4.1+ Javascript toolkit has a complete and rather sophisticated solution that deals with all these issues.  However, it's fairly complex and can be a bit of an overkill for simpler scenarios, especially if the 4000-character limit is not a problem.

Here, I present a pretty simple way of setting up a page that can get a short message with just a little bit of help from the server.  Note that neither solution will allow you to consume arbitrary web services on the Internet - there is just no way to do that without a server-side proxy of some kind.

To work around issue number 2 above, we can create our hidden IFRAME in the top-most window, which is presumably in the same domain as the window/frame that needs to perform the communication.  Since this is fairly straightforward, for clarity, I am just going to omit dealing with this issue :-).

Details:

First, to solve issue number 1 above, on the server side, we use the Referer header sent by the parent window to determine the URL to which we can append our hash string.  In addition, the response generated by the server will include a short bit of Javascript code that sets the parent window's URL to itself, plus a response string after a #.

The nice thing about this approach is we can use the exact same response for requests that are wired to read this message through this method, as well as regular requests such as form submissions that just expect a normal pretty human-readable response. This works because a window will either have no parent or the window's URL will match the Referer and setting its URL to itself will have no ill effects.  Although this example uses Tcl/AOLserver/our own APIs, it's pretty self-explanatory:

# server-side code - set parent URL from Referer header
# and escape special Javascript characters including single quotes:
set parent [ns_set iget [ns_conn headers] Referer]
set message [am_escapeForJavascript $message]

<script language='javascript' type='text/javascript'>
    if (window.parent) {
        window.parent.location.href = '$parent#$message';
    }
</script>

After that, the normal human-readable response body follows. Since this is such a simple and low-impact change on the server, you can have all your responses that you can foresee being used by pages in another domain include this.  To make this easy in AOLserver, our modified version of ns_returnnotice now has an optional parameter for the string to use as $message above.

The following client-side code is needed in the parent window to actually read such as response and place it into a span with id of "response":

<script language='javascript' type='text/javascript'>
function showMessageFromIFrame () {
    var msg = window.location.hash.substring(1);
    if (msg != "") {
        document.getElementById("response").innerText = msg;
        window.location.hash = "";
        clearInterval(document.IFrameMessageIntervalId);
    }
}
//set above function to run every 10 msecs:
document.IFrameMessageIntervalId = setInterval(showMessageFromIFrame, 10)

// here you would submit a form to an IFRAME or replace its location URL
</script>

As you can see, it's not really that complicated.  It gets more complicated if the server needs to respond with more than 4000 characters worth of information.  To be able to do this, the child window would need to first tell the parent window its URL, then break the response into 4000-character chunks and set them one at a time waiting for an acknowledgement that the chunk has been processed, which the parent window would set in the child's hash.

One last thing.  Frequently, it's convenient to be able to create a hidden IFRAME on the fly, especially if you need to create it in the top-most window to work around the IE7 problem.  You can use our createHiddenIFrame function which is based on the code in
http://developer.apple.com/internet/webcontent/iframe.html:

createHiddenIFrame.js

 

© Copyright 1996-2010 by am.net and Solitex Networks. Legal Notices.
Creative Commons License Articles on this site are licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.