You’ve probably heard the term “AJAX” thrown around recently. Basically it’s a fancy name for a technology that’s been around for years. Over the past year or so, however, AJAX-style JavaScript has become increasingly popular among many developers and we’re starting to see some amazing things come from it. Google Maps and GMail are two of the most well known AJAX applications, but companies all over the web are starting to incorporate it into their sites.
AJAX stands for Asynchronous JavaScript And XML. All it really consists of is JavaScript, just like you would have in any other application, and an XMLHTTP connection to your web server. That’s it! The general idea is that when you need to pull data from a server you can just go and get the specific data you need instead of refreshing the entire page. To start with, AJAX almost always relies on a server-side language such as PHP or ASP. When your users need to get new data, the JavaScript requests it, and the server will probably query a database and then return the data. This data can be returned in several forms. If it is structured, then you will generally have XML or JSON data. If it is very simple data (such as getting a description for an item), you will often see people just writing that data directly to the AJAX response.if(typeof(XMLHttpRequest)!='undefined'){ var getXMLHttpObj = function(){ return new XMLHttpRequest(); } } else { var getXMLHttpObj = function(){ var activeXObjects = ['Msxml2.XMLHTTP.6.0', 'Msxml2.XMLHTTP.5.0', 'Msxml2.XMLHTTP.4.0', 'Msxml2.XMLHTTP.3.0', 'Msxml2.XMLHTTP', 'Microsoft.XMLHTTP']; for(var i=0; i<activeXObjects.length; i++){ try{ return new ActiveXObject(activeXObjects[i]); }catch(err){} } } }
Any browser that supports the XMLHttpRequest object (including IE7 when it is released) will use this built-in object. Internet explorer 6 and earlier will fail back to Microsoft’s XMLHttp ActiveX object. Microsoft has many different versions of this object so we will try them all, starting with the newest one.
Now that we have our XMLHttp object, we will place a request to our server:var oXml = getXMLHttpObj(); oXml.open('GET', 'getData.php', true); oXml.onreadystatechange = processingFunction; oXml.send(); function processingFunction(){ if(oXml.readyState!=4) return; // our request is not done // we process the results here. More on that later! }
After the creation of our XMLHttp object, there are 4 additional steps. First we specify the details of our connection with the .open function. The .open function takes 3 arguments: The request type, the URL and a flag that tells the object whether or not to run asynchrounously.
The first argument, the request type, is almost always GET or POST. If you are retreiving data this will usually be GET and if you are submitting a form with AJAX this will often be POST. The asynchrounous flag is a little different than almost anything else in JavaScript. If this flag is set to false our code continues like any other piece of code, that is it waits for our XMLHttp object to finish doing its thing before moving on. If, however, this flag is set to true, our code continues doing whatever we have after our request. Whenever the request state changes the function defined by onreadystatechange is called. What’s the difference? Well if the async flag is set to false, your browser will completely lock while the request is downloading. If you absolutely need the data before doing anything else, do this. If the flag is set to true then the user can continue to use the webpage, but there is no gaurantee as to when the request comes back. It could come back in 1/2 sec or it could come back in 1 minute. It all depends on what your server is doing and the end user’s connection. When everything is ready to go we send the request. Then we wait. If we are running our request sync with our processing function, defined with onreadystatechange, it will be called several times. It is actually called whenever the state of our request changes. There are various states such as sending and loading data, but all we care about is whether or not the data is done downloading. If the readyState == 4, our request is done, so in any other case we just exit the function. Now we have our data…but wait, what data do we have?<xml> <contacts> <person firstname="Joe" lastname="Smith" phone="555-1212" /> <person firstname="Sam" lastname="Stevens" phone="123-4567" /> </contacts> </xml>
JSON:
{contacts:[ {"firstname":"Joe", "lastname":"Smith", "phone":"555-1212"}, {"firstname":"Sam", "lastname":"Stevens", "phone":"123-4567"} ]}
You may notice that the XML notation looks suspiciously like HTML. Well for the most part, it is. HTML and XML are both tag-based languages and they can even be parsed the same way (see the 6th article in this series for more information).
Further, the JSON notation looks suspiciously like plain ordinary JavaScript. JSON stands for JavaScript Object Notation and as such it is just plain ordinary JavaScript. Either notation can just be sent as plain text from your web server. None of the spacing included in these examples is necessary except for the single spaces between attribute names in the person tags (in the XML version). The XML format is a collection of tags, very similiar to HTML. You can have any number of tags nested in one-another and each tag can have any number of attributes: ie firstname, lastname and phone in the example above. There are a few things to watch out for, however:- Every tag must have a closing tag. <contacts>, for example, is closed with </contacts> below it. The person tags are self-contained. The /> at the end effectively acts as an extra closing tag. We could just as easily have written one of these as: <person …></person>.
- XML has a limited character set that you can use. Specifically the following characters are not valid in nodes or attributes and must be replaced:
- & –> &
- < –> <
- > –> >
- " –> "
- ' –> '
The node <group name=”Bill & Paul” />, for example, is invalid and must be replaced with <group name=”Bill & Paul” />
- Data coming from your server must be sent with a content-type of text/xml. If you are retrieving a file with an .xml extension this should happen automatically. If you are retrieving data from a script, you must set this manually:
- For PHP, add the following:
<?php header('Content-type: text/xml'); ?>
- For ASP, add:
<% response.contentType = "text/xml" %>
- For all other languages, add the equivalent content-type header
If this header is not set, the responseXML property of the XMLHttp object will be empty (this property will be explained shortly).
- For PHP, add the following:
- Objects start and end with { and } characters respectively
- Arrays start and end with [ and ] characters respectively
- All strings are enclosed in double quotes, "
- " characters in string must be escaped: "
Put simply, a JSON string has to represent a valid JavaScript object.
Now we will look at how we can parse this data. For the time being, we are just going to create a script to tell us how many contacts we have and alert their information. Let’s start with the XML version by returning to a previous piece of code that we left open:function processingFunction(){ if(oXml.readyState!=4) return; // our request is not done // we process the results here. More on that later! }
Once our script gets into the body of our function, the XMLHttp request is done. The XMLHttp object has two methods to return data: responseXML and responseText. Since we are working with an XML file at the moment, we want to use responseXML:
function processingFunction(){ if(oXml.readyState!=4) return; var xmlDoc = oXml.responseXML; var contacts = xmlDoc.selectNodes('/xml/contacts/person'); alert('There are '+contacts.length+' contacts!'); for(var i=0; i<contacts.length; i++){ alert('Contact #'+(i+1)+':nn'+ 'First Name: '+contacts[i].getAttribute('firstname')+'n'+ 'Last Name: '+contacts[i].getAttribute('lastname') +'n'+ 'Phone #: '+contacts[i].getAttribute('phone') +'n'); } }
We get 3 alerts here. One that tells us there are two contacts, and two more that have the contact information for each person.
Looking at the same script using our JSON text:function processingFunction(){ if(oXml.readyState!=4) return; var json = eval('('+oXml.responseText+')'); alert('There are '+json.contacts.length+' contacts!'); for(var i=0; i<json.contacts.length; i++){ alert('Contact #'+(i+1)+':nn'+ 'First Name: '+json.contacts[i].firstname+'n'+ 'Last Name: '+json.contacts[i].lastname +'n'+ 'Phone #: '+json.contacts[i].phone +'n'); } }
As you can see, JSON strings can be transformed into a JavaScript by simply using the eval() command, built in to JavaScript. You should only do this, however, if you completely trust the source of the data. If you do not (if it is coming from anyone other than yourself), you should run it through a JSON Parser just to be safe.
Finally you could elect to return your data in any other straight text format you choose instead of XML or JSON. If you do this you will have to figure out how to parse the data, however. If you only have one piece of data however, such as a comment, this may be a viable option for you. As for which one to use: XML vs JSON? There’s not a whole lot of difference here. XML is a more wide spread format and is transferable to almost any system. If your project is going to be working with external sources, you would probably use XML. JSON is, however, a bit easier to understand and in general it is faster to develop code for than XML. If you are using this for a personal project or starting a new project that doesn’t need to interface with other applications, JSON is definitely worth consideration.<table cellspacing="1" cellpadding="3" bgcolor="#000000" style="font-family:tahoma;font-size:10px;"> <tbody id="contactListTable"> <tr style="background-color:#CCF;"> <th>First Name</th> <th>Last Name</th> <th>Phone #</th> </tr> </tbody> </table>
function loadContactListPage(n){ var oXML = getXMLHttpObj(); oXML.open('GET', '/img/10_json_file'+n+'.txt', true); oXML.onreadystatechange = function(){ doneLoading(oXML); } oXML.send(''); } function doneLoading(oXML){ if(oXML.readyState!=4) return; var json = eval('('+oXML.responseText+')'); var table = document.getElementById('contactListTable'); for(var i=table.childNodes.length-1; i>0; i--){ table.removeChild(table.childNodes[i]); } for(var i=0; i<json.contacts.length; i++){ var tr = document.createElement('TR'); var td1 = document.createElement('TD'); var td2 = document.createElement('TD'); var td3 = document.createElement('TD'); tr.style.backgroundColor = i%2?'#FFF':'#E6E6E6'; table.appendChild(tr); tr.appendChild(td1); tr.appendChild(td2); tr.appendChild(td3); td1.appendChild(document.createTextNode(json.contacts[i].firstname)); td2.appendChild(document.createTextNode(json.contacts[i].lastname)); td3.appendChild(document.createTextNode(json.contacts[i].phone)); } }