Using Ajax - Learning PHP, MySQL & JavaScript: With jQuery, CSS & HTML5, Fourth Edition (2015)

Learning PHP, MySQL & JavaScript: With jQuery, CSS & HTML5, Fourth Edition (2015)

Chapter 17. Using Ajax

The term Ajax was first coined in 2005. It stands for Asynchronous JavaScript and XML, which, in simple terms, means using a set of methods built in to JavaScript to transfer data between the browser and a server in the background. An excellent example of this technology is Google Maps (see Figure 17-1), in which new sections of a map are downloaded from the server when needed, without requiring a page refresh.

Using Ajax not only substantially reduces the amount of data that must be sent back and forth, but also makes web pages seamlessly dynamic—allowing them to behave more like self-contained applications. The results are a much improved user interface and better responsiveness.

What Is Ajax?

The beginnings of Ajax as used today started with the release of Internet Explorer 5 in 1999, which introduced a new ActiveX object, XMLHttpRequest. ActiveX is Microsoft’s technology for signing plug-ins that install additional software to your computer. Other browser developers later followed suit, but rather than using ActiveX, they all implemented the feature as a native part of the JavaScript interpreter.

However, even before then, an early form of Ajax had already surfaced that used hidden frames on a page that interacted with the server in the background. Chat rooms were early adopters of this technology, using it to poll for and display new message posts without requiring page reloads.

So let’s see how to implement Ajax by using JavaScript.

Google Maps is an excellent example of Ajax in action

Figure 17-1. Google Maps is an excellent example of Ajax in action

Using XMLHttpRequest

Because of the differences between browser implementations of XMLHttpRequest, you must create a special function in order to ensure that your code will work on all major browsers.

To do this, you must understand the three ways of creating an XMLHttpRequest object:

§ IE 5: request = new ActiveXObject("Microsoft.XMLHTTP")

§ IE 6+: request = new ActiveXObject("Msxml2.XMLHTTP")

§ All others: request = new XMLHttpRequest()

This is the case because Microsoft chose to implement a change with the release of Internet Explorer 6, while all other browsers use a slightly different method. Therefore, the code in Example 17-1 will work for all major browsers released over the last few years.

Example 17-1. A cross-browser Ajax function

<script>

function ajaxRequest()

{

try // Non IE Browser?

{ // Yes

var request = new XMLHttpRequest()

}

catch(e1)

{

try // IE 6+?

{ // Yes

request = new ActiveXObject("Msxml2.XMLHTTP")

}

catch(e2)

{

try // IE 5?

{ // Yes

request = new ActiveXObject("Microsoft.XMLHTTP")

}

catch(e3) // There is no AJAX Support

{

request = false

}

}

}

return request

}

</script>

You may remember the introduction to error handling in the previous chapter, using the try...catch construct. Example 17-1 is a perfect illustration of its utility, because it uses the try keyword to execute the non-IE Ajax command, and upon success, jumps on to the final returnstatement, where the new object is returned.

Otherwise, a catch traps the error, and the subsequent command is executed. Again, upon success, the new object is returned; otherwise, the final of the three commands is tried. If that attempt fails, then the browser doesn’t support Ajax and the request object is set to false; otherwise, the object is returned. So there you have it: a cross-browser Ajax request function that you may wish to add to your library of useful JavaScript functions.

OK, so now you have a means of creating an XMLHttpRequest object, but what can you do with these objects? Well, each one comes with a set of properties (variables) and methods (functions), which are detailed in Table 17-1 and Table 17-2.

Property

Description

onreadystatechange

Specifies an event-handling function to be called whenever the readyState property of an object changes.

readyState

An integer property that reports on the status of a request. It can have any of these values: 0 = Uninitialized, 1 = Loading, 2 = Loaded, 3 = Interactive, and 4 = Completed.

responseText

The data returned by the server in text format.

responseXML

The data returned by the server in XML format.

status

The HTTP status code returned by the server.

statusText

The HTTP status text returned by the server.

Table 17-1. An XMLHttpRequest object’s properties

Method

Description

abort()

Aborts the current request.

getAllResponseHeaders()

Returns all headers as a string.

getResponseHeader(param)

Returns the value of param as a string.

open('method', 'url', 'asynch')

Specifies the HTTP method to use (Get or Post), the target URL, and whether the request should be handled asynchronously (true or false).

send(data)

Sends data to the target server using the specified HTTP method.

setRequestHeader('param', 'value')

Sets a header with a parameter/value pair.

Table 17-2. An XMLHttpRequest object’s methods

These properties and methods give you control over what data you send to the server and receive back, as well as a choice of send and receive methods. For example, you can choose whether to request data in plain text (which could include HTML and other tags) or in XML format. You can also decide whether you wish to use the Post or Get method to send to the server.

Let’s look at the Post method first by creating a very simple pair of documents: a combination of HTML and JavaScript, and a PHP program to interact via Ajax with the first. Hopefully you’ll enjoy these examples, because they illustrate just what Web 2.0 and Ajax are all about. With a few lines of JavaScript, they request a web document from a third-party web server, which is then returned to the browser by your server and placed within a section of the current document.

Your First Ajax Program

Type and save the code in Example 17-2 as urlpost.html, but don’t load it into your browser yet.

Example 17-2. urlpost.html

<!DOCTYPE html>

<html>

<head>

<title>AJAX Example</title>

</head>

<body style='text-align:center'>

<h1>Loading a web page into a DIV</h1>

<div id='info'>This sentence will be replaced</div>

<script>

params = "url=amazon.com/gp/aw"

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

}

}

}

return request

}

</script>

</body>

</html>

Let’s go through this document and look at what it does, starting with the first six lines, which simply set up an HTML document and display a heading. The next line creates a DIV 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 urlpost.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 property. 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 function. 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

}

}

Looking at Table 17-1, you’ll see that readyState can have five values. But only one concerns us: value 4, which represents a completed Ajax call. Therefore, 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 displays the error message contained in statusText.

NOTE

You will notice that all of these object properties are referenced using this.readyState, this.status, and so on, rather than the object’s current 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, we test the responseText value 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

In this line, the element info is referenced via 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 via the following command, which passes the parameters already defined in the variable params:

request.send(params)

After that, all the preceding code is activated each time readyState changes.

The remainder of the document is the ajaxRequest function from Example 17-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 17-3. Type it and save it as urlpost.php.

Example 17-3. urlpost.php

<?php // urlpost.php

if (isset($_POST['url']))

{

echo file_get_contents('http://' . SanitizeString($_POST['url']));

}

function SanitizeString($var)

{

$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 be done with all posted data. In this instance, unsanitized data could result in the user gaining an advantage over your code.

This program uses 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, you are ready to call up urlpost.html into your web browser and, after a few seconds, you should see the contents of the Amazon mobile 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 17-2.

The Amazon mobile website has been loaded into a DIV

Figure 17-2. The Amazon mobile website has been loaded into a DIV

Not only have we succeeded in making an Ajax call and having a response returned to JavaScript, but we’ve 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 Amazon mobile web page directly via Ajax (without recourse to the PHP server-side module), we wouldn’t have succeeded, because there are security blocks preventing cross-domain Ajax. So this example also illustrates a handy solution to a 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 the 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 17-4 shows how you would achieve the same result as with Example 17-2, but using an Ajax Get request instead of Post.

Example 17-4. urlget.html

<!DOCTYPE html>

<html>

<head>

<title>AJAX Example</title>

</head>

<body style='text-align: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=amazon.com/gp/aw" + 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)

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 described as follows:

§ It is not necessary to send headers for a Get request.

§ We call the open method using a Get request, supplying a URL with a string comprising a ? symbol followed by the parameter/value pair url=amazon.com/gp/aw.

§ We start a second parameter/value pair using an & symbol, and then set 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, the PHP program must be modified to respond to a Get request, as in Example 17-5, urlget.php.

Example 17-5. urlget.php

<?php

if (isset($_GET['url']))

{

echo file_get_contents("http://".sanitizeString($_GET['url']));

}

function sanitizeString($var)

{

$var = strip_tags($var);

$var = htmlentities($var);

return stripslashes($var);

}

?>

All that’s different between this and Example 17-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, first take a look at the PHP program, xmlget.php, shown in Example 17-6.

Example 17-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 output the correct XML header before returning a fetched document. No checking is made here, as it is assumed that the calling Ajax will request an actual XML document.

Now on to the HTML document, xmlget.html, shown in Example 17-7.

Example 17-7. xmlget.html

<!DOCTYPE html>

<html>

<head>

<title>AJAX Example</title>

</head>

<body>

<h1>Loading a web page into a DIV</h1>

<div id='info'>This sentence will be replaced</div>

<script>

nocache = "&nocache=" + Math.random() * 1000000

url = "rss.news.yahoo.com/rss/topstories"

out = "";

request = new ajaxRequest()

request.open("GET", "xmlget.php?url=" + url + nocache, true)

request.onreadystatechange = function()

{

if (this.readyState == 4)

{

if (this.status == 200)

{

if (this.responseText != 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

}

}

}

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, responseXML will contain the XML returned.

However, responseXML doesn’t simply contain a string of XML text: it is actually a complete XML document object that we can examine and parse 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 Example 17-8. However, the beauty of XML is that we can store this type of structure internally in a DOM tree (see Figure 17-3) to make it quickly searchable.

Example 17-8. An XML document

<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0">

<channel>

<title>RSS Feed</title>

<link>http://website.com</link>

<description>website.com's RSS Feed</description>

<pubDate>Mon, 11 May 2020 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>

The DOM tree of Example 17-8

Figure 17-3. The DOM tree of Example 17-8

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 17-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 processed, 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 17-4.

Fetching a Yahoo! XML news feed via Ajax

Figure 17-4. Fetching a Yahoo! XML news feed via Ajax

NOTE

As with all form data, you can use either the Post or the Get method when requesting XML data; your choice will make little difference to the result.

Why use XML?

You may ask why you would use XML other than for fetching XML documents such as RSS feeds. Well, the simple answer is that you don’t have to, but if you wish to pass structured data back to your Ajax applications, it could be a real pain to send a simple, unorganized jumble of text that would need complicated processing in JavaScript.

Instead, you can create an XML document and pass that back to the Ajax function, which will automatically place it into a DOM tree, as easily accessible as the HTML DOM object with which you are now familiar.

Using Frameworks for Ajax

Now that you know how to code your own Ajax routines, you might like to investigate some of the free frameworks that are available to make it even easier, and which offer many more advanced features. In particular, I would suggest you check out jQuery, probably the most commonly usedframework, and which I introduce in Chapter 21. In the following chapter, though, we’ll look at how to apply styling to your websites with CSS.

Questions

1. Why is it necessary to write a function for creating new XMLHttpRequest objects?

2. What is the purpose of the try...catch construct?

3. How many properties and how many methods does an XMLHttpRequest object have?

4. How can you tell when an Ajax call has completed?

5. How do you know whether an Ajax call completed successfully?

6. What XMLHttpRequest object’s property returns an Ajax text response?

7. What XMLHttpRequest object’s property returns an Ajax XML response?

8. How can you specify a callback function to handle Ajax responses?

9. What XMLHttpRequest method is used to initiate an Ajax request?

10.What are the main differences between an Ajax GET and POST request?

See Chapter 17 Answers in Appendix A for the answers to these questions.