header header header
the book
the book
projects articles   Simple AJAX
  lm_sensors
  /dev/fanout
contact downloads

A Short Tutorial on XMLHttpRequest()

(How to Build Event Driven Web Pages)

By Bob Smith

 

Introduction

What you will learn: This tutorial will show you how to build a web page that responds to asynchronous events. In five simple exercises, we show you the core of an Asychronous JavaScript And XML (AJAX) application. You will learn a little JavaScript, but will not need or learn anything about XML. We don't require XML anywhere in this tutorial. We promise to keep it simple. Our goal is to show you how to build a responsive web application while forcing you to learn as little as possible.

What you should already know: You should already know how to build a web page and about the client/server nature of web page requests. You should understand how CGI programs work and should have some experience with CGI programming. We use about seven lines of PHP in our exercises but you can replace this with C or Perl if you wish.

Requirements for the examples: To do the exercises in this tutorial you will need a browser with JavaScript enabled. JavaScript is enabled by default on most browsers so this requirement shouldn't require any action on your part. You will also need to have a web server and the ability to write web pages for the web server. The web server needs to support CGI and PHP. You should be ready to go if you already have Apache and PHP installed on your PC, and our exercises assume that you're using your PC for the web server.

 

Application Overview

To illustrate the need for AJAX, let's consider a real-world example. We've built a telephone answering machine and we want the caller ID information to be presented on a web page. A web interface lets us view the status of the answering machine while we are at home or at work. The data flow in our application looks something like this:

   (answering_machine -> syslog -> fifo -> Apache -> web_page)

Our answering machine logs caller ID information to syslog using LOG_LOCAL3, a facility not used by any other applications on our system. A syslog rule in syslog.conf directs messages with this facility to a fifo located in the Apache document directory. This tutorial deals with getting the strings written to the fifo to display on a web page. Writing to a fifo is the asynchronous part of our sample application.

 

Getting Started

In the first exercise we are going to verify your development environment by running a simple PHP application. Copy the following PHP program to getfifo.php in the document directory of your web server.

<?php
  $fp = fopen("ajaxfifo", "r");
  if ($fp) {
    $ajaxstring = fgets($fp, 128);
    fclose($fp);
  }

  header("Content-Type: text/html");
  print($ajaxstring);
?>
Create a fifo in the same directory.
mkfifo ajaxfifo
Try to view getfifo.php using your web browser. Your web browser should hang waiting for the read of ajaxfifo to complete. Write some data to the fifo before the browser times out.
echo "Hello, World" > ajaxfifo
The text that you entered should appear in your browser window. Continue on to the next exercise if the data was displayed properly. If the data did not appear, go back and verify that the web server and PHP are installed and working properly.

 

DOM in Two Lines of Code

JavaScript is an object oriented language and treats a displayed web page as an object. The Document Object Model, or DOM, describes the objects, properties, and verbs (or methods) available you using JavaScript. Using DOM you can name different parts of a web page and can then perform different actions on those parts.

The following exercise verifies that JavaScript is running on your browser by creating a named region on a web page, and putting some text into that named region. Copy the following program to hello.html in the document directory of your web server.

<html>
<head>
<title>Exercise 2: Use DOM to Test JavaScript</title>

<script language=javascript type="text/javascript">
<!-- Hide script from non-JavaScript browsers

  // SayHello() prints a message in the document's "hello" area
  function SayHello() {
    document.getElementById("hello_area").innerHTML="Hello, World";
    return;
  }

-->
</script>
</head>

<body onload="SayHello()">
<h3><center>Exercise 2:  Use DOM to Test JavaScript</center></h3>
</p>
<div id="hello_area">This text is replaced.</div>
</body>
</html>
The above code identifies a section of the web page as "hello_area" and uses the subroutine SayHello() to put text in the named section. Load hello.html and verify that your browser's JavaScript displays "Hello, World" on the web page.

 

Callbacks in JavaScript

A callback is a subroutine that is called when a particular event occurs. JavaScript has a fairly complete set of events to which you can attach a callback. The following program starts a timer using a callback that is invoked when the page is loaded. A callback associated with the timer displays a counter and restarts itself. Copy this program to callback.html in your server's document directory, and verify that the count is updated in your browser window every two seconds.

<html>
<head>
<title>Exercise 3: A JavaScript Callback Demo</title>

<script language=javascript type="text/javascript">
<!-- Hide script from non-JavaScript browsers
  var count;
  count = 0;

  // Put the current count on the page
  function DisplayCount() {
    // Put current time in the "count" area of the web page
    document.getElementById("count_area").innerHTML=
      "The count is: " + count++;

    // Schedule next call to DisplayCount
    setTimeout("DisplayCount()", 2000);
    return;
  }
-->
</script>
</head>

<body onload="DisplayCount()">
<h3><center>Exercise 3: A JavaScript Callback Demo</center></h3>
<div id="count_area">This text is replaced.</div>
</body>
</html>

 

How to use XMLHttpRequest()

XMLHttpRequest is a JavaScript subroutine that lets you make an HTTP request and attach a callback to the response. Since the response has a callback, the browser is free to continue responding to user input. In a way, XMLHttpRequest is to JavaScript what select() is to C, a way to wait for an event. XMLHttpRequest() is what puts the 'A' in AJAX.

The following exercise adds only XMLHttpRequest() to the previous exercises. We request the data from getfifo.php using XMLHttpRequest() and we tie a callback to the arrival of the response. The response callback displays the data using a named division per the DOM. Copy the following code into async.html.

<html>
<head>
<title>Exercise 4: An XMLHttpRequest() Demo</title>

<script language=javascript type="text/javascript">
<!-- Hide script from non-JavaScript browsers

  var req_fifo;

  // GetAsyncData sends a request to read the fifo.
  function GetAsyncData() {
    url = "getfifo.php";

    // branch for native XMLHttpRequest object
    if (window.XMLHttpRequest) {
      req_fifo = new XMLHttpRequest();
      req_fifo.abort();
      req_fifo.onreadystatechange = GotAsyncData;
      req_fifo.open("POST", url, true);
      req_fifo.send(null);
    // branch for IE/Windows ActiveX version
    } else if (window.ActiveXObject) {
      req_fifo = new ActiveXObject("Microsoft.XMLHTTP");
      if (req_fifo) {
        req_fifo.abort();
        req_fifo.onreadystatechange = GotAsyncData;
        req_fifo.open("POST", url, true);
        req_fifo.send();
      }
    }
  }

  // GotAsyncData is the read callback for the above XMLHttpRequest() call.
  // This routine is not executed until data arrives from the request.
  // We update the "fifo_data" area on the page when data does arrive.
  function GotAsyncData() {
    // only if req_fifo shows "loaded"
    if (req_fifo.readyState != 4 || req_fifo.status != 200) {
      return;
    }
    document.getElementById("fifo_data").innerHTML=
      req_fifo.responseText;

    // Schedule next call to wait for fifo data
    setTimeout("GetAsyncData()", 100);
    return;
  }

-->
</script>

</head>
<body onload="GetAsyncData()">
<h3>Exercise 4: An XMLHttpRequest() Demo</h3><p>
<div id="fifo_data"> </div>

</body>
</html>
Load async.html and, if all has gone well, everything sent to ajaxfifo will appear in the browser window. For example:
echo "Hello, AJAX" > ajaxfifo
Sample screen for Exercise 4

 

An Appliance Web UI

If you were building an appliance (such as our answering machine) you'd want your web UI to respond to events on the appliance. The exercises above show how to do this. But you might also want your appliance UI to have some controls available to the user. The final exercise below adds code to the previous exercise to show you how to tie buttons in a web form to JavaScript subroutines. This exercise demonstrates that a web UI can wait for asynchronous events and still be interactive.

Copy async.html to webui.html and Add the following subroutine into the JavaScript code:

  // setColor updates the "color_area" with the color specified
  function setColor(new_color) {
    color_text = "<table border=2 bgcolor=";
    if ("Blue" == new_color) {
      color_text += "\"Blue\">"
    }
    else if ("Red" == new_color) {
      color_text += "\"Red\">"
    }
    else {       // shouldn't get here
      color_text += "\"Green\">"
    }
    color_text += "<tr><td>A little color</td></tr></table>";
    document.getElementById("color_area").innerHTML=color_text;
  }
Add a form to the HTML of webui.html:
<body onload="GetAsyncData()">
<h3>Exercise 5: A Trivial Web UI</h3><p>
<div id="fifo_data"> </div>
<p>
<div id="color_area">No color yet.</div><p>
<form>
<input type=button value="Blue" onClick="setColor('Blue')">
<input type=button value="Red" onClick="setColor('Red')">
</form>
</body>

 

Summary and Notes

Summary This short tutorial has shown you the fundamentals for building responsive, interactive web interfaces using JavaScript and XMLHttpRequest(). In order to focus on the core concepts we've ignored many important details, such as error handling and return codes. We hope you can add the techniques shown in this article to your web toolbox.

 

Fifo versus /dev/fanout: Open a two windows with async.html and write some data into the fifo. Notice that the data that you write appears in only one window. This is because the fifo passes the data to only one of the readers. In our real application we use a fanout device so that the data is written to all readers. Fanout is described in a previous Linux Gazette article (here) and on the author's home page (here).

 

XML: If you are getting only a single piece of data from your web server, you can use the code in this article. As soon as you ask for more than one piece of data, you should switch to XML. It does not need to be difficult.

Let's say that you've modified getfifo.php to use the caller ID to look up the time and duration of the last time you spoke with the caller, and instead of returning just the caller ID string, you want to return the last call time and duration. If we ignore the PHP code to do the database lookup, the PHP to build an XML response for the three pieces of information might look like this:

<?php
  header("Content-Type: text/xml");
  print("<?xml version=\"1.0\" ?>\n");
  print("<caller_info>\n");
  printf("<caller_id>%s</caller_id>\n",       "Mary");
  printf("<lastcalltime>%s</lastcalltime>\n", "10:24 am");
  printf("<lastcalldur>%s</lastcalldur>\n",   "12:05");
  print("</caller_info>\n");
?>
Instead of using req_fifo.responseText to get the whole body of the reply, you would use the following to extract each of the three fields you want:
  callid = req_fifo.responseXML.getElementsByTagName("caller_id");
  calltm = req_fifo.responseXML.getElementsByTagName("lastcalltime");
  calldur = req_fifo.responseXML.getElementsByTagName("lastcalldur");
  callid[0].childNodes[0].nodeValue;

  document.getElementById("caller_data").innerHTML=
    callid[0].childNodes[0].nodeValue;
  document.getElementById("lastcalltime").innerHTML=
    calltm[0].childNodes[0].nodeValue;
  document.getElementById("lastcalldur").innerHTML=
    calldur[0].childNodes[0].nodeValue;
There are simpler and better ways to do this, but the above code should give you an idea of what to expect.

 

Further reading: The Apple developer's web site has a good article on XMLHttpRequest(). You can find it here:
    http://developer.apple.com/internet/webcontent/xmlhttpreq.html

You may find the tutorials at the JavaScript Kit web site to be a big help if filling in the details that this article omits. You can find their tutorials here:
    http://www.javascriptkit.com/javatutors/

The IBM DeveloperWorks web site has some great articles on AJAX and XML. We found this one particularly useful:
    http://www.ibm.com/developerworks/web/library/wa-ajaxintro1.html

Finally, you might be interested in the low cost answering machine that's at the heart of the author's appliance:
    http://www.linuxtoys.org/answer/answering_machine.html