Coding with Jesse

Cross-Domain JSON without XHR

August 11st, 2006

I was just reading on the Ajaxian a quote from an article Why XHR should become opt-in cross-domain, so I started thinking, isn't there a way around this already?

A quick explanation of what I'm talking about: XMLHttpRequest (the function behind Ajax) will only let you connect to URLs on the same domain as the page you're on. This means the Ajax happening on xyz.com can only connect to URLs on xyz.com. Even if XYZ wanted to use Ajax to pull some data from foobar.com, they wouldn't be able to.

Now there is already one popular way around this. You can make a proxy page that gets the data for you. Using the same example, XYZ could make a URL like xyz.com/json_proxy that pulled data over from foobar.com behind the scenes, thus making the foobar.com data available on xyz.com. The problem with this is, XYZ would have to handle double the bandwidth of this data unnecessarily. This can be a big problem for smaller companies.

I have an alternative solution to this problem. Instead of using XMLHttpRequest to load the data, we can just use <script> tags instead. These don't have the same cross-server restrictions as XMLHttpRequest. In fact, this is how Adsense works. You stick a <script> tag on the page which executes JavaScript coming from Google's server, and this writes out the ads on the page.

To add a <script> tag to our page, we only need to call this function:

function addScript(url) {
    var script = document.createElement('script');
    script.src = url;
    document.body.appendChild(script);
}

If we have a URL that returns some JSON data, we just pass that URL to this function and the page will load that data. Well, not entirely. There's a few things we need to do different. With JSON, you have some data like:

{
   "firstname": "Jesse",
   "lastname": "Skinner"
}

and then use XMLHttpRequest with eval() to assign it to a variable. If you just stuck a script tag on the page to load this, nothing would happen. It would just load and the JavaScript parser would say "Great, nice little piece of data." But it's not going to assign it to a variable, so you'll have no way to access it.

There's actually no way around this unless you change what is returned by the JSON data URL. This is the special trick. We would need the JavaScript to execute some function or assign the data to a variable. It would need to look like this:

json_callback({
   "firstname": "Jesse",
   "lastname": "Skinner"
});

Now, we just need to make a simple function called json_callback() that takes the data and does something with it. Voila! Cross-server JSON.

Rather than hardcode some callback function name, it would probably be better to add an optional "callback" parameter that would specify the function name. If the callback parameter is missing, the server can return standard ol' JSON.

Let's say our simple JSON example above came from "foobar.com/data?format=json". If example.com was kind enough to provide this flexibility, they could add the parameter so that "foobar.com/data?format=json&callback=my_json_callback" formats the data as a function call, passing the data object to my_json_callback().

Now, we just need to wait for web APIs with JSON to start supporting this parameter.

Update: I've since discovered that Yahoo!'s JSON API already provides a 'callback' parameter exactly as I've described above, and I suspect that other APIs out there may support this as well. Either I'm psychic or there are time travellers working at Yahoo! ;) Here are the details from Yahoo!