Examine the code that follows, which is a fragment listing of a web page:
<body onload=”LoadCustomers();”> <form id=”form1” runat=”server”> <div>
<select id=”ddlCustomers” onchange=”DisplayCustomerDetails();”> <option value=””>- Select a Customer -</option>
</select> <hr /> <div>
<p>Details:</p>
<span id=”spnDetailDisplay”>(You have not made a selection yet.)</span> </div>
</div> </form> </body> </html>
Within the ASP.NET form element <form id=”form1” runat=”server”>is a <select>element that acts as your customer drop-down list and a <span>element where you can display the customer details. You will also notice that the onloadevent of the document has a LoadCustomers()function assigned to it to initially load in the list of customers and that the onchangeevent of the <select>item has a
DisplayCustomerDetails()function assigned to it to display the selected customers details once a selection is made.
Firefox does not currently support the use of the selectSingleNode()and selectNodes()methods to access data within an XML document. To address this, the script include library includes some JavaScript code to enable this support. The code included was taken from the site http://km0ti0n.blunted.co.uk/mozXPath .xap. A full explanation of the details of this support is beyond the scope of this chapter; suffice it to say that it allows support of the selectSingleNode()and selectNodes()methods in the same way that Internet Explorer does.
88
Next listed in the following code blocks are the JavaScript functions that accompany the page listing. The first function is a generic function to simply create an XMLHttpRequest object that you can use:
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <html xmlns=”http://www.w3.org/1999/xhtml” >
<head runat=”server”>
<title>Untitled Page</title> <script type=”text/javascript”>
// A “global” variable that is our XMLHttpRequest object reference. var xmlHttpObj = CreateXmlHttpRequestObject();
The next function is what is called when the document first loads and deals with loading the customer data from the server using the XMLHttpRequestobject:
// Function to load the customer selection data into the <SELECT> drop list control function LoadCustomers()
{
if (xmlHttpObj) {
// We want this request synchronous
xmlHttpObj.open(“GET”,”http:// “ + location.host + “/XmlHttp_Chap4/DataFile.xml”, false);
// Execute the request xmlHttpObj.send(null);
// If the request was ok (ie. equal to a Http Status code of 200) if (xmlHttpObj.status == 200)
{
var xmlDoc = xmlHttpObj.responseXML;
// Our list of <CUSTOMER> nodes selected using the X Path argument //var nodes = xmlDoc.selectNodes(“//Customers/Customer”);
var nodes = xmlDoc.selectNodes(“//Customers/Customer/Lastname/text()”); // Obtain a reference to the <SELECT> drop list control.
var ctrl = document.getElementById(“ddlCustomers”); for (var i=0; i < nodes.length; i++)
{
// Get the lastname element from our XML data document var lastName = nodes[i].nodeValue;
// Create a new <OPTION> node.
var htmlCode = document.createElement(‘option’); // Add the new <OPTION> node to our <SELECT> drop list ctrl.options.add(htmlCode);
// Set the <OPTION> display text and value; htmlCode.text = lastName;
htmlCode.value = lastName; }
} else
89
{
alert(‘There was a problem accessing the Customer data on the server.!’);
} } }
In the preceding code, you will notice the use of a literal number 200 in the following line of code:
if (xmlHttpObj.status == 200)
This is another perfect candidate to place into the common script include file. The code can be defined in the following manner:
/* Common values for HTTP status codes */ var HTTPSTATUS_OK = 200;
which means that any code comparing the HTTP status codes as in the previous example can be replaced with the following line of code:
if (xmlHttpObj.status == HTTPSTATUS_OK)
This makes the code much easier to read and maintain.
Finally, you have the JavaScript function that deals with displaying a customer’s details once a selection is made from the drop-down list:
function DisplayCustomerDetails() {
if (xmlHttpObj) {
// We want this request asynchronous
xmlHttpObj.open(“GET”,”http:// “ + location.host + “/XmlHttp_Chap4/DataFile.xml”, true); xmlHttpObj.onreadystatechange = function() { if ( xmlHttpObj.readyState == READYSTATE_COMPLETE ) { var ctrl = document.getElementById(“ddlCustomers”); var doc = xmlHttpObj.responseXML;
var lastName = ctrl.options[ctrl.selectedIndex].value;
var node = doc.selectSingleNode(“//Customers/Customer[Lastname=’” + lastName + “‘]”);
var details = ‘Fullname: ‘ +
node.selectSingleNode(‘Firstname/text()’).nodeValue + ‘ ‘ + lastName + ‘. Email: ‘ + node.selectSingleNode(‘email/text()’).nodeValue; document.getElementById(“spnDetailDisplay”).childNodes[0].nodeValue = details; }
90
Chapter 4
}
// Execute the request xmlHttpObj.send(null); } } </script> </head> </head>
Here, you have opted to define the XMLHttpRequestobject as a “global” variable xmlHttpObj, rather than redeclaring and creating this object in each function. The function to assign a valid XMLHttpRequest
instance to the variable is separated into its own discrete function. This might typically be included as part of your common script library rather than having to rewrite this in every page.
Using a “global” object to hold a reference to your XMLHttpRequestobject can expose your client- side application to a potential bug or flaw in its operation if a subsequent request is issued using the same XMLHttpRequestobject before the first request has completed.
In addition, since the LoadCustomers()function is executed as part of the load event of the page, and the page is not usable until this function has executed, you make the server call in a synchronous manner using the falseparameter, as shown in the line below:
// We want this request synchronous
xmlHttpObj.open(“GET”,”http://” + location.host + “/XmlHttp_Chap4/DataFile.xml”, false);
You execute the call, and then you check the status of the call by examining the statusproperty of the
XMLHttpRequestobject. This property contains the standard HTTP status code returned by the server as a result of the request being made. In this example, you check to ensure that a status code of 200 was returned as part of the call:
// If the request was ok (ie. equal to a Http Status code of 200) if (xmlHttpObj.status == 200)
{
// rest of function . . .
The status code of 200 returned from the server indicates a successful server request has been made. Because you have defined this literal value in the script include file, the code would read:
// If the request was ok (ie. equal to a Http Status code of 200) if (xmlHttpObj.status == HTTPSTATUS_OK)
{
// rest of function . . .
For a list of all the valid HTTP status codes defined by the W3C, see www.w3.org/Protocols/ rfc2616/rfc2616-sec10.html.
You then use the XML DOM method selectNodesto execute an X Path expression over the XML data to find each Lastnamenode for each Customer node and return a list of matching nodes:
91
var nodes = xmlDoc.selectNodes(“//Customers/Customer/Lastname/text()”); // Obtain a reference to the <SELECT> drop list control.
var ctrl = document.getElementById(“ddlCustomers”); for (var i=0; i < nodes.length; i++)
{
// rest of function . . .
You iterate over the list of Lastnamenodes, adding each customer’s last name to the drop-down list.
Within the DisplayCustomerDetails(), you define a function that is executed when the appropriate
readystateof the request has been reached (readystate == READYSTATE_COMPLETE). You then use an X Path expression to locate the node matching your selected customer, extract the details of the cus- tomer from the XML data file using the selectSingleNodemethod, and display those details within the <span>element.
The previous example introduced the statusproperty that is part of the XMLHttpRequestobject. The
XMLHttpRequestobject does not have an extensive list of properties and methods and is relatively sim- ple and straightforward. The two tables that follow are, respectively, a reference table of the methods and one of properties available for the XMLHttpRequestobject.
Method Description
abort() Cancels the current request.
getAllResponseHeaders() Returns the complete set of HTTP headers as a string.
getResponseHeader(“headername”) Returns the value of the specified HTTP header.
open(“method”,”URL”, “async”, Specifies the method, URL, and other optional
”uname”,”pswd”) attributes of a request.
The method parameter can have a value of GET,
POST, or PUT(use GETwhen requesting data and use POSTwhen sending data — especially if the length of the data is greater than 512 bytes). The URL parameter may be either a relative or complete URL.
The asyncparameter specifies whether the request should be handled asynchronously or not. true
means that script processing carries on after the
send()method, without waiting for a response.
falsemeans that the script waits for a response before continuing script processing.
send(content) Sends the request.
setRequestHeader(“label”,”value”) Adds a label/value pair to the HTTP header to be sent.
92
Property Description
onreadystatechange An event handler for an event that fires at every state change. (Read/Write)
readyState Returns the state of the object: (read-only)
0 = uninitialized 1 = loading 2 = loaded 3 = interactive 4 = complete
responseText Returns the response as a string (read-only)
responseXML Returns the response as XML. This property
returns an XML document object, which can be examined and parsed using W3C DOM node tree methods and properties (read-only)
status Returns the status as a number (e.g., 404 for “Not
Found” or 200 for “OK”, 500 for “Server Error”) (read-only)
statusText Returns the status as a string (e.g., “Not Found” or
“OK”) (read-only)