Update (8/22/2008): Evaluated Cross-Domain AJAX with DOMAssistant.
AJAX applications have become commonplace, but many of the tutorials for developing them lean heavily on the help of JavaScript frameworks like DOMAssistant or Prototype. So I figured I would take a look at the standard AJAX-related properties and methods, see how cross-domain calls make the whole process slightly more complicated, and find out how much the frameworks can actually help. Without the help of a framework, you basically have three steps to display content obtained via AJAX:
- Create a
XMLHttpobject - Setup and send the request
- Display the resulting content
Creating a XMLHttp Object
XMLHTTP was originally created by Microsoft and is still in use today. Other major browsers have adopted the same functionality, but in their XMLHttpRequest object. Since the functionality of both objects is almost identical, we only need to take the separate initialization into account for our code to work cross-browser:
var req;
// Get the XMLHttp object based on browser support
function getReqObject() {
// If it supports XMLHttpRequest, use that
if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
// Fix a bug for certain browsers that support this object
if (req.overrideMimeType) {
req.overrideMimeType('text/xml');
}
}
// Otherwise (IE)
else if (window.ActiveXObject) {
// Try two versions
try {
req = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
req = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
// Make sure we have the object
if (!req) {
alert('Could not create an XMLHTTP instance.');
return false
} else {
return req
}
}
Setting Up and Sending the Request
The next step is to set up the request, send it, and specify a function to handle the result:
// Create and send an XMLHttp request
function doRequest(url, postVars) {
// Get the request object (see previous function)
req = getReqObject()
// Make sure we have a XMLHttp object created
if (req) {
// If it's a post, we have to create and send the request slightly differently.
if (postVars) {
req.open('POST', url, true);
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.send(postVars);
} else {
req.open('GET', url, true);
req.send(null);
}
// Set the function that handles the results
req.onreadystatechange = function () { displayContents(req) };
}
}
// Request via GET
doRequest('test.php?var=1', null);
// Request via POST
doRequest('test.php', 'var=1');
Notice that the code is slightly different depending on whether you submit using GET or POST. You can read more about the merits of using GET vs. POST, but basically use GET unless the submission updates a data source, or if it is very large.
We also specified a function to fire whenever the “ready state,” of the request changes. This is the function that will handle the result.
Handling the Result
We’ve sent the request, now we need to create the function to do something with the result:
// Display resulting content from req
function displayContents(req) {
// Only do something when the request is done
if (req.readyState == 4) {
// Make sure it was a success
if (req.status == 200) {
// Traverse the resulting XML, alerting titles of items
var xml = req.responseXML;
var items = xml.getElementsByTagName("item");
for (var i = 0; i < items.length; i++) {
alert(items[i].getElementsByTagName('title').item(0).firstChild.nodeValue);
}
} else {
alert('Problem with the request.');
}
}
}
This function ensures the request is completed and that is successful, then traverses the resulting document, alerting the "titles" of "items." Obviously you'll have to substitute the method of crawling the DOM depending on the resulting document. But that should be it right? Well, not exactly.
Cross-Domain Requests
Due to security concerns, the previous code will only work for requests within your particular domain. So what if you want to grab data from an RSS feed hosted on an outside site? You've got a couple of options:
- Create a server-side proxy to grab content
- Use apache's
mod_rewriteormod_proxyto mask outside requests - Hope the service you're trying to access can output JSON
- Digitally sign your scripts.
We can't rely on services providing JSON output because it's not widely adopted yet, and we can't digitally sign our scripts, because browser support for that is limited. Using mod_rewrite is definitely an option, but a server-side proxy is more extensible given the additional options for server-side processing before the content gets back to our JavaScript. Here's a basic PHP proxy, which will accept a URL and (optionally) post variables from a JavaScript call, and return the content.
<?php
// Set URL we want to access
$url = ( $_POST['url'] ? $_POST['url'] : $_GET['url'] );
// Open Curl session
$session = curl_init($url);
// Set params if posted
if ($_POST['params']) {
$postvars = '';
foreach ($_POST['params'] as $postkey=>$postvalue) {
$postvars .= $postkey . '=' . $postvalue . '&';
}
curl_setopt($session, CURLOPT_POST, true);
curl_setopt($session, CURLOPT_POSTFIELDS, $postvars);
}
// Don't return HTTP headers, do return contents
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
// Get the file
$xml = curl_exec($session);
// Display content
if ($xml) {
header("Content-Type: text/xml");
echo $xml;
} else {
echo 'No response';
}
// Clean up
curl_close($session)
?>
Now we can make external requests with our JavaScript:
// External request using proxy via GET
doRequest('proxy.php?url=http://externalsite.com/feed.php', null);
// External request using proxy via POST
doRequest('proxy.php', 'url=http://externalsite.com/feed.php');
That's it! Next time I'll take a look at some JavaScript frameworks and see how they can be used to simplify some of this process.
