Technetra

Snappier Web Applications with XMLHttpRequest

Nilayan Sharma,  April 27th, 2005 at 6:40 pm

As web applications take on roles traditionally served by pure desktop applications, creating interactive user interfaces with the “real-time” feel of a customary desktop, is a challenge facing UI developers today. Unlike desktop application interfaces, which are tightly connected to the application, web application interfaces communicate with the user via HTTP, a stateless protocol. Normally, any update to a web application interface, after it has been downloaded to the client, requires a full request-response cycle back to the server. In this article, we will explore a solution to this problem using JavaScript and the XMLHttpRequest object.

XMLHttpRequest is not brand new technology, however, it recently has been revived through excellent implementations like Google Mail and Google Suggest. These new services show us the power of the browser as a cross-platform development environment. Furthermore, the combination of client-side callbacks with DHTML and JavaScript reduces the need for specialized Java Applets and or ActiveX controls.

What is XMLHttpRequest?

The XMLHttpRequest object was first implemented by Microsoft in Internet Explorer 5 for Windows as an ActiveX object. Subsequently, developers on the Mozilla project and Apple’s Safari browser implemented a compatible native object within their respective browsers, starting with Mozilla 1.0 and Safari 1.2.

The XMLHttpRequest object enables JavaScript functions to exchange HTTP transactions with a remote server completely in the background. Besides the visual benefit of not reloading pages between requests, using XMLHttpRequest also results in an overall reduction in bandwidth needed between client and server, as only information being updated is requested from the server.

Several web clients do not provide complete support for XMLHttpRequest. Graphical web clients lacking support include Opera and Internet Explorer on Macintosh. In addition, most terminal-based clients (e.g., lynx, links, w3m) do not support JavaScript at all. Developers targeting their web applications for these clients should provide alternate pages.

An example: ZIP code reverse-lookup

Now let’s create a simple web application using JavaScript and the XMLHttpRequest object to retrieve the name of the post office associated with a zip code. First, the user picks a zip code from a dropdown list (<select> element). Next, we use the XMLHttpRequest object to query a simple PHP script on the server. The PHP script matches the zip code to a post office and returns its name. Finally, we display this name back to the user. All this without any page reloads.

Our web application consists of two files: an HTML page and a dynamic page written as a simple PHP script. These files must be placed under the appropriate document root of your web server. The HTML page consists of a simple HTML form and three JavaScript functions. The PHP script responds to JavaScript requests, initiated by the client’s browser, that have been embedded in the HTML page. The major components of our example are described below.

  • Components of File: xmlhttpdemo.html
    • HTML form — Standard HTML <form> which contains the drop-down list (<select id="zip">) of zip codes.
    • makeHttpObject() — JavaScript function that instantiates the XMLHttpRequest object.
    • getHttpResponse() — JavaScript function that receives the HTTP response from the server-side script. This response contains the results of the zip code reverse-lookup.
    • getPoName(ev) — JavaScript function that sends the HTTP request, containing the selected zip code, to the server-side PHP script. This function is triggered by the onChange event of the zip code (<select id="zip" onChange="getPoName(event)">) list in the HTML form.
  • Components of File: doLookup.php — Server-side PHP script
    • PHP script that executes on the server, and is invoked via the XMLHttpRequest object.
    • The single parameter ‘zip’, provided via the URL query string, specifies the zip code to look up. (doLookup.php?zip=110014)
    • If the zip code is matched successfully, the return value is a string containing the post office name, otherwise an error message is returned.

1. HTML form

The first and easiest part of our web application is the HTML form which presents a drop-down list of zip codes for the user to pick from. We assign the getPoName() function (which we’ll define later) as the onChange event handler of the zip code drop-down list. This function is triggered anytime the user picks a value from the list. The <div id="post_office"> tag below the form is where the name of the post office will be displayed once it is retrieved from the server-side script.

<form id="demo">
  <label for="zip">Select ZIP code:</label>
  <select id="zip" name="zip" size="1" onChange="getPoName(event)">
    <option value="110001">110001</option>
    <option value="110003">110003</option>
    <option value="110005">110005</option>
    <option value="110006">110006</option>
    <option value="110007">110007</option>
    <option value="110008">110008</option>
    <option value="110009">110009</option>
    <option value="110010">110010</option>
    <option value="110011">110011</option>
    <option value="110013">110013</option>
    <option value="110014">110014</option>
    <option value="110015">110015</option>
    <option value="220002">220002</option> <!-- an invalid entry -->
  </select>
</form>
<div id="post_office">
  <span></span>
</div>

2. JavaScript function to create XMLHttpRequest object

As I mentioned earlier the XMLHttpRequest object was created by Microsoft and then later implemented in Mozilla and Safari. Therefore each environment instantiates this object differently, as seen in the makeHttpObject() function. Internet Explorer uses an ActiveX object,var httpObj = new ActiveXObject("Microsoft.XMLHTTP"); whereas both Mozilla and Safari use a native object, var httpObj = new XMLHttpRequest();

Due to these differences it is best to write a function that hides the specific kind of object that must be instantiated. Within the function, makeHttpObject(), the correct object can be created by checking for Microsoft’s JScript version level and then checking which object (ActiveX or native) can be instantiated.

<script language="javascript" type="text/javascript">
function makeHttpObject() {
    var xmlHttpObj;

    // branch for Activex version (Microsoft IE)
    /*@cc_on
    @if (@_jscript_version >= 5)
    try {
        xmlHttpObj = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        try {
            xmlHttpObj = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (E) {
            xmlHttpObj = false;
        }
    }
    @else
         xmlHttpObj = false;
    @end @*/
    // branch for native XMLHttpRequest object (Mozilla & Safari)
    if (!xmlHttpObj && typeof XMLHttpRequest != 'undefined') {
        try {
            xmlHttpObj = new XMLHttpRequest();
        } catch (e) {
            xmlHttpObj = false;
        }
    }
    return xmlHttpObj;
}

var httpObj = makeHttpObject(); // create the HTTP Object
</script>

3. JavaScript function to handle HTTP response

Next, we define the function getHttpResponse(), which will handle the HTTP response from the server. We assign this function as the onreadystatechange event handler of the XMLHttpRequest object. When called, it examines two properties of the XMLHttpRequest object. First, we check the readyState property. Numeric codes (0=uninitialized, 1=loading, 2=loaded, 3=interactive, 4=complete) represent the status of the object. If the value is ‘4‘, we can proceed further. Next, we check if the status property. This is the numeric HTTP status code returned by the server. If the value is ‘200‘, which represents ‘OK‘, our request was successful. Now we can access the actual response via the responseText property of the XMLHttpRequest object, and insert this value between the <div id="post_office"></div> tags using the Document Object Model (DOM). If you know that the response will be XML, use the responseXML property instead. It represents the XML response as a document node object, which you can parse using the DOM.

<script language="javascript" type="text/javascript">
function getHttpResponse() {
    if (httpObj.readyState == 4) {
        if (httpObj.status == 200) {
            content = httpObj.responseText;
            div = document.getElementById("post_office");
            div.innerHTML = "";
            // insert HTML content into "post_office" <div>
            div.innerHTML = content;
        } else {
            alert("There was a problem with the response" + httpObj.statusText);
        }
    }
}
</script>

4. JavaScript function to handle HTTP request

Now we define the onChange event handler function for the zip code drop-down list and the URL of the server-side PHP script. In order to comply with the standard JavaScript security model, we need to be careful when defining the server-side script URL, as the domain must be the same as the one that serves up the HTML page containing the XMLHttpRequest object script. Unfortunately, this means client-side scripts cannot fetch information from other sources. Everything must come from the same domain.

Inside the getPoName() function, we assign the onreadystatechange event handler (getHttpResponse), that we mentioned before, and submit the GET request for our zip code lookup. The open() method requires two parameters. The first parameter (method) specifies the HTTP method we wish to use for the request. For this parameter, use ‘GET‘ for requests that retrieve data, and use ‘POST‘ for requests that send data. The second parameter (URL) specifies a complete or relative URL for the connection. The third, optional but important, parameter (asyncFlag) specifies whether the request should be handled asynchronously. By default, processing occurs asynchronously, right after the send() method is invoked, without waiting for a response. If set to false, processing waits for a response from the server, which may cause your script to hang if a network or server problem occurs. In general, it is best to handle requests asynchronously and handle responses based on the onreadystatechange event of the request object.

<script language="javascript" type="text/javascript">
var url = "doLookup.php?zip="; // URL for server-side PHP script
function getPoName(ev) {
    ev = (ev) ? ev : ((window.event) ? window.event : null);
    if (ev) {
        var el = (ev.target) ? ev.target : ((ev.srcElement) ? ev.srcElement : null);
        if (el) {
            if (el.selectedIndex > 0) {
                httpObj.open("GET", url + el.options[el.selectedIndex].value, true);
                httpObj.onreadystatechange = getHttpResponse;
                httpObj.send(null);
            }
        }
    }
}
</script>

5. JavaScript function to handle HTTP

Finally, we create the PHP file doLookup.php which maps the zip code received in the URL query string to a post office name using a hash table. If the zip code is found, a string value containing the post office name is returned. Otherwise a simple error message is returned.

<?php
// doLookup.php: maps ZIP code in $_GET['zip'] to name of post office
$post_offices = array('110001'=>'New Delhi G.P.O',
                      '110003'=>'Lodhi Road G.P.O',
                      '110005'=>'Karol Bagh',
                      '110006'=>'Delhi G.P.O',
                      '110007'=>'Malkaganj',
                      '110008'=>'Paschim Nagar',
                      '110009'=>'Guru Tejh Bahadur Nagar',
                      '110010'=>'Delhi Cantt.',
                      '110011'=>'D.H.Q',
                      '110013'=>'Sarojini Nagar G.P.O',
                      '110014'=>'Jangpura',
                      '110015'=>'Ramesh Nagar G.P.O');

if (isset($post_offices[$_GET['zip']])
    echo "Post office: ", $post_offices[$_GET['zip']];
else
    echo "Sorry, invalid zip code.";
?>
Zip code lookup (lookup results shown in green)

Zip code lookup (lookup results shown in green)

Running the example

Launching our web application in the browser (http://url_of_your_web_server/xmlhttpdemo.html) should show something like the Figure A below. Once a zip code is selected from the drop-down list, you should see something like Figure B. Try selecting the last item (220002) from the zip code list, an error message should appear instead of the post office name.

Conclusion

We have seen that using the XMLHttpRequest object to retrieve information from remote sources is pretty straight forward. In appropriate Web applications, the XMLHttpRequest model can dramatically reduce delays associated with the traditional request/response cycle of full page-based transactions. Although the example I used was quite simple, I hope it encourages you to think about more innovative ways to utilize XMLHttpRequest in your next project.

© Nilayan Sharma, Technetra. Published April 2005 in LinuxForYou magazine. This work is licensed under a Creative Commons Attribution-No Derivative Works 3.0 License. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

Innovation or Patent Colonialism Article Index Tamil Language Open Source Tools & Apps released in Chennai

Comments

2 Responses to “Snappier Web Applications with XMLHttpRequest”

  1. January 15th, 2008 at 9:04 am (Comment)
    vishal bhite Says:

    best notes for ajax beginner

  2. October 22nd, 2008 at 10:10 am (Comment)
    Anonymous User Says:

    Excellent,
    The way you structure the information makes it very easy to follow and learn.
    Thankyou.

© 2000-2009 Technetra. All rights reserved. Contact | Terms of Use

WordPress