The "Hello World" of Ajax can be intimidating because there is more than one language involved: PHP and JavaScript. Unlike in a traditional web application where most of the presentation and data processing is handled by PHP (or any server-side language for that matter), in Ajax applications PHP only creates the data and it is up to the JavaScript on the client side to interpret that data.
If you are unfamiliar with PHP 5 you may want to take a moment to look at the Appendix for an overview of what relevant features have changed between versions 4 and 5.
After receiving a request to an Ajax-enabled PHP page, the server then returns the page as it normally would. The magic occurs when we include JavaScript in the served page that is linked to actions. Once one of those actions occurs, an asynchronous request will be made to the server. The server then returns the requested data, which JavaScript can act on accordingly (see Figure 1).
We can see that the actor (client viewing your web page) makes a request to view the selected page where he is served a mix of HTML, JavaScript, and most likely images. The JavaScript is designed so that by using Ajax the user can perform an action and receive data in return without leaving or reloading the page he is currently viewing.
Because the page is being served by PHP, we have the benefit of a powerful scripting language on our side. In this example, we serve a page to the web browser and then request and display the system date and time from the server without ever leaving or reloading the page.
The PHP is basic. If the client passes the variable "NOW" via the GET method, then the server will return the date and time, as well as a little message to, hopefully, bring back some memories. POST and GET are the two primary methods (in addition to HEAD and several others) of retrieving a page via HTTP. They each have the added ability of being able to pass variables to the server. In the case of GET, the variables are stored in the URL encoded as a query string (the part of the URL that appears after the first question mark).
We want to create a page that displays the time on the server when the user clicks a button. To do this we have a simple PHP script:
<?php function get_message() { return "Hello world! It is ".date("r"); } if ( isset( $_GET["NOW"] ) ) { header("Cache-Control: no-cache, must-revalidate"); header("Content-type: text/plain"); echo get_message(); } else { ?> <html> <head> <title>Hello world</title> <!-- insert JavaScript here --> </head> <body> <div id="data"> <? if ( isset($_GET["NOW-INLINE"]) ) echo get_message(); else echo "Press Go"; ?> </div> <a href="?NOW-INLINE" id="action">Go</a> </body> </html> <?php } ?>
Let's analyze this file. If you know HTML, you should recognize the metadata inside the "else" block as HTML formatted information. The page isn't anything special. However, here our focus is on functionality, not on aesthetics.
The function get_message()
returns the
formatted date and time. We moved the echo to a separate function
because it is used twice: when the $_GET["INLINE"]
variable is set and when NOW-INLINE
is set.
Put this page in a directory that is accessible via your web server. Then load it into a browser on your client computer. Assuming PHP is configured properly, you will see a page with the text "Press Go" and a link that says "Go" under to it. Now click on the link. You should be taken to a page that reads "Hello world! It is Mon, 12 Feb 2007 13:43:00 −0600. Go." If you see that page with your server's current date and time, then we are off to a good start. However, this page currently doesn't contain any JavaScript and certainly doesn't contain any asynchronous requests. Next, we add JavaScript to make this example a complete Ajax application.
This example uses in-file JavaScript. Other examples in the text
will store the JavaScript in external files. For now, the JavaScript
code should be inserted where the <!-- insert JavaScript
here -->
comment is in the previous PHP script:
<script type="text/javascript"> <!-- var httpObj=null; function OnLoad() { document.getElementById("action").href="javascript:Update()"; } function Update() { if ( httpObj != null ) return; document.getElementById("data").innerHTML = "Loading"; httpObj = NewHTTP(); httpObj.open("GET","?NOW",true); httpObj.onreadystatechange = OnData; httpObj.send(null); } function NewHTTP() { try { return new XMLHttpRequest(); } catch (e) { return new ActiveXObject("Microsoft.XMLHTTP"); } } function OnData() { if ( httpObj.readyState==4 ) { m=document.getElementById("data"); if (httpObj.status==200 ) { m.innerHTML = httpObj.responseText; } else { m.innerHTML="Error loading date and time."; } httpObj = null; } } --> </script>
The first function called is OnLoad()
and it
should be executed when the page finishes loading. We will see more of
it later in the "Unobtrusive JavaScript
section."
The meat and bones of the script is the
Update()
function, which will be called when the go
button is clicked. This event will perform several tasks.
Check to make sure the httpObj
variable is
null. If it isn't, then a request is already occurring so don't
resend the same request.
Set the text inside our box to "Loading." Even though the script will function without it, this step is important and should not be left out because it provides immediate feedback to the user. Without it, the user may not know that the data is being transferred in the background and could become frustrated. A frustrated user may press the Refresh button or leave the web application altogether. Whenever possible, always provide this type of feedback to avoid confusing or frustrating the user. For example, in Web 2.0 applications this is typically done with a ball revolving in a circle to signify "Loading" until the content transfer is complete.
Create a new XMLHttpRequest object via the user defined
NewHTTP()
function.
Call the open()
method, which tells the
object the specific page to request from the server. Be aware that,
at this point, no data has been sent to the server. The first
parameter is the method used to retrieve the file. As hinted at
earlier, this is commonly either GET or POST.
Specify the actual URL to load. This can be done using a
relative or absolute URL. In a relative URL, we specify only the
path to the file in relation to the file the user is currently
viewing. If we were using an absolute URL, then we would include the
full path to the file. In this case we, want to fetch the current
page using the query string "NOW" so we use the relative URL "?NOW"
in our request. This tells the browser to fetch the current page
with the query string. Also in the open()
method,
we specify that this is an asynchronous request by using
true
. If the third parameter is
false
, the script halts execution until after the
file is loaded and then continues. Since it is
true
, the script resumes execution immediately
and leaves status handling to the callback function.
Assign the callback function. A callback function is a
user-defined function that is stored and later called when it is
needed, allowing us to define our own code that executes when an
event happens. In this case, the function is called when an HTTP
event occurs that affects the ready state of
the request. In our example, it is OnData()
. The
ready state can be thought of as the status of the request.
Make the actual request for the page from the server, now that we have all the information necessary. The send method makes the request. If the object is asynchronous (which it is in this example), then this method returns immediately. Otherwise, it waits for the page to load or a failure to occur.
Microsoft invented the XMLHTTPRequest object in the form of an ActiveX control called XMLHTTP. It is an object that makes it possible for the client to request files using the HTTP protocol. If the file is XML, then it will be automatically interpreted as such.
The other browser manufacturers saw this and implemented the same functionality. Unfortunately, Internet Explorer is the only major browser that natively uses ActiveX controls. (Therefore, the XMLHTTPRequest object was born.) The Internet Explorer version behaves the same but it is initialized differently than the way most other browsers initialize it. This, unfortunately, muddies the waters when creating new request objects. For this reason, we move the creation of new objects to a separate function so we can more easily create multiple requests without duplicating the four lines of code it takes to create the object. Most browsers—including Microsoft's Internet Explorer 7—recognize and use the XMLHttpRequest object, however, if that fails (e.g., on Internet Explorer 6), we fall back to the XMLHTTP ActiveX object, which works only in Internet Explorer. Use the try/catch block to do this as you saw previously in the NewHTTP function.
Create the XMLHttpRequest before creating the ActiveX control because, as of Internet Explorer 7, you can use either; initialize the one that is most likely to be successful.
It can sometimes be confusing to work with multiple languages in the same project because PHP 4 does not have exception handling (try/catch). However, this is not a limitation in JavaScript. You can use try/catch in your JavaScript regardless of the version of PHP you use.
We already set a callback function to be executed whenever the ready state of the request changes. This callback function is seen in the form of "OnData" in the example.
You may have noticed that we have a global variable httpObj that stores the instance of the XMLHTTPRequest. We reference this object again in the OnData function.
The "readyState" property can be 0 to 4 where 0 is initialized and the number 4 is analogous to complete. In this example, we only worry about state 4. However, we still need to check to make sure it is actually complete (4) because the function can and will be called with some of the other values as well. Once the document has loaded completely, we get a pointer to the div (box) we created earlier. Next, we check to see if the page load was successful by checking the status code. Status code is a standard HTTP response code. You probably recognize 404 as the "File not found" error. We want to get a 200 response, which lets us know the operation was successful.
Finally, free the XMLHTTPRequest object by setting the only variables referencing it to null. This frees up some of the memory and helps prevent leaks and slowdown that can occur in some browsers by leaving the object initialized for too long.
If you insert the JavaScript code into your file, you may notice that clicking the "Go" link still reloads the page just as before instead of asynchronously fetching the information. The reason for this is that we haven't changed the href of the link, so none of our nice new JavaScript is called. If you already changed the link, then take a step back for a minute. Congratulations, you now have a working Ajax program. However, there is one small issue.
Changing the link in the HMTL makes it useless for people with JavaScript disabled. This may seem trivial but some people choose not to use JavaScript and some browsers don't allow JavaScript. Failing to accommodate these people can lose you potential customers. Instead use unobtrusive JavaScript.
Unobtrusive JavaScript is a term coined to describe JavaScript that does not interfere with the style or layout elements of the page. It is also fault tolerant and degrades gracefully on less-than-capable browsers (e.g., browsers with JavaScript disabled).
That is where the "onLoad" event comes into play. Change the body tag on your page to look like this:
<body onLoad="OnLoad();">
This tells your browser to call the OnLoad()
function when all the elements of a page finish loading. In this case,
all the function does is change the href of the
link on the client-side to equal the appropriate JavaScript call.
Obviously, you cannot use unobtrusive JavaScript in all cases—such as when working with an existing system—but, whenever you can, you should consider it. Ultimately, it is up to the webmaster to decide if she wants a site that's accessible to both people with and without JavaScript capabilities.
Sometimes if you have a fast server and a low latency connection to it, the page will load exceptionally fast so that you barely (if at all) notice when the transition elements appear. In the previous example, we displayed a "Loading" message, which goes away once the asynchronous request finishes. However, when we test the script on some servers, the loading image will appear then quickly disappear in the blink of an eye. When this happens, new Ajax users may have a tough time believing that any asynchronous action is occurring at all.
To demonstrate the concepts a bit more completely we can quickly add one line of PHP to our script.
First, we find the line of code that checks for the GET variable
NOW
. Then we alter it so that it appears like code
below:
if ( isset( $_GET["NOW"] ) ) { sleep(5);
Now when the asynchronous request is made, the server will pause
for five seconds and then send the content. This simulates an
exceptionally long server lag, which may happen if the web site is being
hit by thousands of visitors. The 5
can be changed to
any positive number.
One side effect of this lag is that the user may think the page is frozen or not responding. To help with this, use an animated GIF instead of the text "Loading" to display the status to the user.
If all went well, you should now have a working Ajax and PHP application. Obviously, this particular application could be improved. For one, the script is not entirely unobtrusive. We could, for instance, move the JavaScript and CSS to an external file to further separate the script and the data/layout. However, you should now have a complete enough understanding of the concepts to be able to follow the rest of this Short Cut.
In this section, we covered:
The basics of the XMLHTTPRequest object.
What ready states are and how to use the ready states and callback functions to handle a request response once it is received.
How to serve up different content depending on how the page was requested.
98.82.120.188