« Difficulty at the "easycenter" | Main| Spurned by Prototype »

SnTT: Proxyless cross-domain AJAX via DOM scripting

Category show-n-tell thursday

Strictly speaking, AJAX is impossible when spanning domains due to browser restrictions preventing cross-site scripting ("XSS"). Although security issues make this prudent, this does limit our ability to allow web applications to retrieve data from other sites when there is a legitimate need to do so. The standard workaround is to use a proxy to load the remote data: an AJAX request is issued to the server, instructing it which data to pull; the server retrieves the data and sends it back to the browser. There are two drawbacks to this approach:
  • Data retrieval is often slower. The obvious reason for this is that the browser has to wait for the server to retrieve the data instead of just retrieving it directly. But there's another performance implication: instead of simply serving its own data, it's also loading remote data for all users requesting it.
  • Firewall restrictions on servers - particularly intranet servers - are often tighter than those placed on clients. Although the user may have access to the destination domain, the server may not, in which case the lookup will simply fail.
Another option is available: DOM scripting. Many sites (like this one) include script tags from multiple domains, extending the capabilities of the web site / application. But DOM scripting allows these tags to be added on the fly as the need arises, a concept typically referred to as "On-Demand Javascript". A few examples of this technique can be found here. The following Javascript function illustrates how this is done:

function loadODJ() {
var headTag = document.getElementsByTagName('head')[0];
var scriptTag = document.createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.src = arguments[0]; // the script file's URL is passed to the function
headTag.appendChild(scriptTag);
}

On the example page included above, the first example attempts to use XMLHttpRequest to load an HTML page on timtripcony.net from a page on timtripcony.com. Both domains reside on the same server, but the browser doesn't know that; it sees two different domains and blocks the request.

When a script tag is added to the DOM, its source is loaded and then executed immediately. This allows us some intriguing options for dealing with remote data. The second example appends a script tag that points to a script file that includes a single alert statement. The alert is executed rapidly, because there's very little data for the browser to download, and it doesn't have to tell the server to load the data and then wait for it to do so.

The third example is a bit more interesting: a variable named loadCount has already been declared in the header of the example page, initialized with a value of 0. The script file that this example loads increments this variable's value and alerts the result. Script loaded in this way becomes part of the page's global execution scope, so it can access existing global variables and functions... and define new ones, which can be referenced later by other code within the page or in other dynamically loaded script.

The final example is the most interactive, and comes closest to demonstrating a real-world use case. When the script is loaded, a query string parameter is included in the script's URL based on a value selected from a drop-down list. The remote script is dynamically generated: the query string parameter becomes the key for a DbLookup. An additional parameter specifies which global function to use as a callback; once the DbLookup is complete, the result data is passed to the callback function, which alerts the result. In Domino this dynamic script generation is easy to do, because design elements like agents, pages, and forms can masquerade as .js files as long as their Content-Type is set correctly. One scenario in which this kind of integration would be useful is when deploying a web application that will be used in-house by numerous organizations but must still be able to access centralized data. This presents an opportunity to provide a dynamic API: no matter which servers host the application, they come to the central server to retrieve the current data.

To sum up, DOM scripting allows us to overcome the cross-domain limitation of XMLHttpRequest, but requires that we have at least some control over both domains involved. Enjoy.

Comments

Gravatar Image1 - Thanks for the explanation, I just needed such info =)

In the examples on the first button I get an error: 'Ajax' is undefined..

Gravatar Image2 - Thanks for this tip!!!

Most requests I've had for Ajax stuff involved data from other domains.
I'll check this out asap

Contact Me

Hire Me

Elsewhere

What the Quote?

"Papa Felpie's? That's an odd name for a restaurant."

Brent Bowers

"...but if you call it 'Operation Butter Hammock', nobody knows what you're talking about."

Adam Slagle

"It's kind of like a Purple Nurple, but with less pain"

Steven Rodgers

"I'm not going to forget that for the same reason scars don't heal."

Tim Tripcony

"Beware the nards of May."

Laura Tripcony

Apparel

Lotus Rocks

I write the code that makes the young girls cry

Current Terror Alert Level

Assorted Linkage

ClustrMap