Example 18-2. urlpost.html <html><head><title>Ajax Example</title> </head><body><center /> <h1>Loading a web page into a DIV</h1> <div id='info'>This sentence will be replaced</div> <script> params = "url=oreilly.com" request = new ajaxRequest() request.open("POST", "urlpost.php", true) request.setRequestHeader("Content-type", "application/x-www-form-urlencoded") request.setRequestHeader("Content-length", params.length) request.setRequestHeader("Connection", "close") request.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { if (this.responseText != null) { document.getElementById('info').innerHTML = this.responseText } else alert("Ajax error: No data received") } else alert( "Ajax error: " + this.statusText) } } request.send(params) function ajaxRequest() { try { var request = new XMLHttpRequest() } catch(e1) { try { request = new ActiveXObject("Msxml2.XMLHTTP") } catch(e2) { try { request = new ActiveXObject("Microsoft.XMLHTTP") } catch(e3) { request = false Using XMLHttpRequest | 381 } } } return request } </script></body></html> Let’s go through this document and look at what it does, starting with the first three lines, which simply set up an HTML document and display a heading. The next line creates an HTML DIV tag with the ID “info”, containing the text “This sentence will be replaced” by default. Later on, the text returned from the Ajax call will be inserted here. The next six lines are required for making an HTTP POST Ajax request. The first sets the variable params to a parameter=value pair, which is what we’ll send to the server. Then the Ajax object request is created. After this, the open method is called to set the object to make a POST request to geturl.php in asynchronous mode. The last three lines in this group set up headers that are required for the receiving server to know that a POST request is coming. The readyState property Now we get to the nitty-gritty of an Ajax call, which all hangs on the readyState prop- erty. The “asynchronous” aspect of Ajax allows the browser to keep accepting user input and changing the screen, while our program sets the onreadystatechange property to call a function of our choice each time readyState changes. In this case, a nameless (or anonymous) inline function has been used, as opposed to a separate, named func- tion. This type of function is known as a callback function, as it is called back each time readyState changes. The syntax to set up the callback function using an inline, anonymous function is as follows: request.onreadystatechange = function() { if (this.readyState == 4) { // do something } } If you wish to use a separate, named function, the syntax is slightly different: request.onreadystatechange = ajaxCallback function ajaxCallback() { if (this.readyState == 4) { // do something } } 382 | Chapter 18: Using Ajax Looking at Table 18-1, you’ll see that readyState can have five different values. But only one of them concerns us: value 4, which represents a completed Ajax call. There- fore, each time the new function gets called, it returns without doing anything until readyState has a value of 4. When our function detects that value, it next inspects the status of the call to ensure it has a value of 200, which means that the call succeeded. If it’s not 200, an alert pop up is displayed containing the error message contained in statusText. You will notice that all of these object properties are referenced using this.readyState, this.status, and so on, rather than the object’s cur- rent name, request, as in request.readyState or request.status. This is so that you can easily copy and paste the code and it will work with any object name, because the this keyword always refers to the current object. So, having ascertained that the readyState is 4 and the status is 200, the responseText value is tested to see whether it contains a value. If not, an error message is displayed in an alert box. Otherwise, the inner HTML of the DIV is assigned the value of responseText, like this: document.getElementById('info').innerHTML = this.responseText What happens in this line is that the element “info” is referenced using the getElementByID method and then its innerHTML property is assigned the value that was returned by the Ajax call. After all this setting up and preparation, the Ajax request is finally sent to the server using the following command, which passes the parameters already defined in the var- iable params: request.send(params) After that, all the preceding code is activated each time readyState changes. The remainder of the document is the ajaxRequest method from Example 18-1, and the closing script and HTML tags. The server half of the Ajax process Now we get to the PHP half of the equation, which you can see in Example 18-3. Type it in and save it as urlpost.php. Example 18-3. urlpost.php <?php // urlpost.php if (isset($_POST['url'])) { echo file_get_contents("http://".SanitizeString($_POST['url'])); } function SanitizeString($var) { Using XMLHttpRequest | 383 $var = strip_tags($var); $var = htmlentities($var); return stripslashes($var); } ?> As you can see, this is short and sweet, and also makes use of the ever-important SanitizeString function, as should always be done with all posted data. What the program does is use the file_get_contents PHP function to load in the web page at the URL supplied to it in the POST variable $_POST['url']. The file_get_contents function is versatile, in that it loads in the entire contents of a file or web page from either a local or a remote server—it even takes into account moved pages and other redirects. Once you have typed the program in, you are ready to call up urlpost.html into your web browser and, after a few seconds, you should see the contents of the oreilly.com front page loaded into the DIV that we created for that purpose. It won’t be as fast as directly loading the web page, because it is transferred twice: once to the server and again from the server to your browser. The result should look like Figure 18-2. Figure 18-2. The oreilly.com front page has been loaded into a DIV 384 | Chapter 18: Using Ajax Not only have we succeeded in making an Ajax call and having a response returned back to JavaScript, we also harnessed the power of PHP to enable the merging in of a totally unrelated web object. Incidentally, if we had tried to find a way to fetch the oreilly.com web page directly via Ajax (without recourse to the PHP server-side mod- ule), we wouldn’t have succeeded, because there are security blocks preventing cross- domain Ajax. So this little example also illustrates a handy solution to a very practical problem. Using GET Instead of POST As with submitting any form data, you have the option of submitting your data in the form of GET requests, and you will save a few lines of code if you do so. However, there is a downside: some browsers may cache GET requests, whereas POST requests will never be cached. You don’t want to cache a request because the browser will just redisplay what it got last time instead of going to the server for fresh input. The solution to this is to use a workaround that adds a random parameter to each request, ensuring that each URL requested is unique. Example 18-4 shows how you would achieve the same result as with Example 18-2, but using an Ajax GET request instead of POST. Example 18-4. urlget.html <html><head><title>Ajax GET Example</title> </head><body><center /> <h1>Loading a web page into a DIV</h1> <div id='info'>This sentence will be replaced</div> <script> nocache = "&nocache=" + Math.random() * 1000000 request = new ajaxRequest() request.open("GET", "urlget.php?url=oreilly.com" + nocache, true) request.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { if (this.responseText != null) { document.getElementById('info').innerHTML = this.responseText } else alert("Ajax error: No data received") } else alert( "Ajax error: " + this.statusText) } } request.send(null) Using XMLHttpRequest | 385 function ajaxRequest() { try { var request = new XMLHttpRequest() } catch(e1) { try { request = new ActiveXObject("Msxml2.XMLHTTP") } catch(e2) { try { request = new ActiveXObject("Microsoft.XMLHTTP") } catch(e3) { request = false } } } return request } </script></body></html> The differences to note between the two documents are highlighted in bold, and are as follows: • It is not necessary to send headers for a GET request. • The open method is called using a GET request, supplying a URL with a string comprising a ? symbol followed by the parameter/value pair url=oreilly.com. • A second parameter/value pair is started using an & symbol, followed by setting the value of the parameter nocache to a random value between 0 and a million. This is used to ensure that each URL requested is different, and therefore that no requests will be cached. • The call to send now contains only a parameter of null as no parameters are being passed via a POST request. Note that leaving the parameter out is not an option, as it would result in an error. To accompany this new document, it is necessary to modify the PHP program to re- spond to a GET request, as in Example 18-5, urlget.php. Example 18-5. urlget.php <?php if (isset($_GET['url'])) { echo file_get_contents("http://".sanitizeString($_GET['url'])); } 386 | Chapter 18: Using Ajax function sanitizeString($var) { $var = strip_tags($var); $var = htmlentities($var); return stripslashes($var); } ?> All that’s different between this and Example 18-3 is that the references to $_POST have been replaced with $_GET. The end result of calling up urlget.html in your browser is identical to loading in urlpost.html. Sending XML Requests Although the objects we’ve been creating are called XMLHttpRequest objects, so far we have made absolutely no use of XML. This is where the Ajax term is a bit of a misnomer, because the technology actually allows you to request any type of textual data, only one of which is XML. As you have seen, we have requested an entire HTML document via Ajax, but we could equally have asked for a text page, a string or number, or even spreadsheet data. So let’s modify the previous example document and PHP program to fetch some XML data. To do this, take a look at the PHP program first, xmlget.php, shown in Exam- ple 18-6. Example 18-6. xmlget.php <?php if (isset($_GET['url'])) { header('Content-Type: text/xml'); echo file_get_contents("http://".sanitizeString($_GET['url'])); } function sanitizeString($var) { $var = strip_tags($var); $var = htmlentities($var); return stripslashes($var); } ?> This program has been very slightly modified (shown in bold highlighting) to first out- put the correct XML header before returning a fetched document. No checking is made here, as it is assumed the calling Ajax will request an actual XML document. Now on to the HTML document, xmlget.html, shown in Example 18-7. Example 18-7. xmlget.html <html><head><title>Ajax XML Example</title> </head><body> <h2>Loading XML content into a DIV</h2> <div id='info'>This sentence will be replaced</div> Using XMLHttpRequest | 387 <script> nocache = "&nocache=" + Math.random() * 1000000 url = "rss.news.yahoo.com/rss/topstories" request = new ajaxRequest() request.open("GET", "xmlget.php?url=" + url + nocache, true) out = ""; request.onreadystatechange = function() { if (this.readyState == 4) { if (this.status == 200) { if (this.responseXML != null) { titles = this.responseXML.getElementsByTagName('title') for (j = 0 ; j < titles.length ; ++j) { out += titles[j].childNodes[0].nodeValue + '<br />' } document.getElementById('info').innerHTML = out } else alert("Ajax error: No data received") } else alert( "Ajax error: " + this.statusText) } } request.send(null) function ajaxRequest() { try { var request = new XMLHttpRequest() } catch(e1) { try { request = new ActiveXObject("Msxml2.XMLHTTP") } catch(e2) { try { request = new ActiveXObject("Microsoft.XMLHTTP") } catch(e3) { request = false } } } 388 | Chapter 18: Using Ajax return request } </script></body></html> Again, the differences have been highlighted in bold, so you can see that this code is substantially similar to previous versions, except that the URL now being requested, rss.news.yahoo.com/rss/topstories, contains an XML document, the Yahoo! News Top Stories feed. The other big change is the use of the responseXML property, which replaces the responseText property. Whenever a server returns XML data, responseText will return a null value, and responseXML will contain the XML returned instead. However, responseXML doesn’t simply contain a string of XML text: it is actually a complete XML document object that can be examined and parsed using DOM tree methods and properties. This means it is accessible, for example, by the JavaScript getElementsByTagName method. About XML An XML document will generally take the form of the RSS feed shown in Exam- ple 18-8. However, the beauty of XML is that this type of structure can be stored in- ternally in a DOM tree (see Figure 18-3) to make it quickly searchable. Figure 18-3. The DOM tree of Example 18-8 Example 18-8. An XML document <?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <channel> <title>RSS Feed</title> Using XMLHttpRequest | 389 <link>http://website.com</link> <description>website.com's RSS Feed</description> <pubDate>Mon, 16 May 2011 00:00:00 GMT</pubDate> <item> <title>Headline</title> <guid>http://website.com/headline</guid> <description>This is a headline</description> </item> <item> <title>Headline 2</title> <guid>http://website.com/headline2</guid> <description>The 2nd headline</description> </item> </channel> </rss> Therefore, using the getElementsByTagName method, you can quickly extract the values associated with various tags without a lot of string searching. This is exactly what we do in Example 18-7, where the following command is issued: titles = this.responseXML.getElementsByTagName('title') This single command has the effect of placing all the values of the “title” elements into the array titles. From there, it is a simple matter to extract them with the following expression (where j is the title to access): titles[j].childNodes[0].nodeValue All the titles are then appended to the string variable out and, once all have been pro- cessed, the result is inserted into the empty DIV at the document start. When you call up xmlget.html in your browser, the result will be something like Figure 18-4. Figure 18-4. Fetching a Yahoo! XML news feed via Ajax 390 | Chapter 18: Using Ajax . a look at the PHP program first, xmlget .php, shown in Exam- ple 1 8-6 . Example 1 8-6 . xmlget.php <?php if (isset($_GET['url'])) { header('Content-Type: text/xml'); echo. it as urlpost.php. Example 1 8-3 . urlpost.php <?php // urlpost.php if (isset($_POST['url'])) { echo file_get_contents("http://".SanitizeString($_POST['url'])); } function. modify the PHP program to re- spond to a GET request, as in Example 1 8-5 , urlget.php. Example 1 8-5 . urlget.php <?php if (isset($_GET['url'])) { echo file_get_contents("http://".sanitizeString($_GET['url'])); } 386