Section VII.3. Using XMLHttpRequest


VII.3. Using XMLHttpRequest

If the XMLHttpRequest object causes consternation among first-timers, it's primarily because of two characteristics that are not like other DHTML objects you work with. First, you have to follow a specific sequence of operations to get the object to work correctly; second, it operates entirely in the background, leaving you to use your best JavaScript debugging tools and skills to solve problems. This section covers the first part, while debugging is addressed later.

VII.3.1. Overview

The sequence of operations is not complicated. More code of a simple function responsible for loading an XML document is devoted to browser syntax differences than any other aspect.

All operations are performed on an instance of the XMLHttpRequest object, just like you do for the JavaScript Date objectspawning an instance that has specific information about just one "live" copy of the object. For the sake of convenience throughout this discussion, I'll call this instance a request object.

The following sequence is the typical way you work with a request object:

  1. Null out a variable holding a previous request object (if any).

  2. Create a new instance of the XMLHttpRequest object, assigning it to a variable.

  3. Open the request object, specifying the type of request and URL.

  4. Bind the request object to an event handler function that is to run upon completion of data retrieval.

  5. Optionally, specify a request header, such as content-type.

  6. Send the request.

  7. Process the results, inspecting one or more properties of the request object containing the data.

In most applications, each instance of a request object should be stored as a global variable (or as a global custom object that incorporates the XMLHttpRequest mechanism code). This allows other functions to access the data of the request object.

Example VII-1 is a listing of an object-based approach to working with the XMLHttpRequest object. It allows for both reuse of the same instance for retrieving multiple XML documents as well as multiple instances if you wish to keep several instances running at once (although bear in mind that browsers can use no more than two simultaneous connections to the same domain). The next few sections refer to Example VII-1.

Example VII-1. Custom XMLHttpRequest object constructor function

 // constructor function for an XML request object; function XMLDoc( ) {     var me = this; // for event handler parameter, below     var req = null;     // branch for native XMLHttpRequest object     if (window.XMLHttpRequest) {         try {             req = new XMLHttpRequest( );         } catch(e) {             req = null;         }     // branch for IE/Windows ActiveX version     } else if (window.ActiveXObject) {         try {             req = new ActiveXObject("Msxml2.XMLHTTP");         } catch(e) {             try {                req = new ActiveXObject("Microsoft.XMLHTTP");             } catch(e) {                req = null;             }         }     }     // preserve reference to request object for later     this.request = req;     // "public" method to be invoked whenever     this.loadXMLDoc = function(url, loadHandler) {         if (this.request) {             this.request.open("GET", url, true);             this.request.onreadystatechange = function ( ) {loadHandler(me)};             this.request.setRequestHeader("Content-Type", "text/xml");             this.request.send("");         }     }; } // create request object instances var request1 = new XMLDoc( ); var request2 = new XMLDoc( ); 

VII.3.2. Creating a Request Object

Thanks to browser implementation differences, more code lines go into creating an instance of a request object than any other part of the process of retrieving an XML document. These are the opening lines of the XMLDoc( ) constructor function shown in Example VII-1. The top branch is for the native global XMLHttpRequest object so that browsers that implement both ways (such as IE 7) use the native object. The other branches load the ActiveX version, giving preference to the more modern version, Msxml2.XMLHTTP.

VII.3.3. Request Object Methods

In typical operation, you primarily use methods of a request object to prepare the request and send it on its way. Different browsers implement different sets of methods (see Chapter 2 of Dynamic HTML, Third Edition), but for basic operation you can rely on the lowest common denominator of methods, dictated by Microsoft's earliest implementations. Table VII-1 lists those methods.

Table VII-1. Lowest common denominator methods of a request object

Method

Description

abort( )

Stops current request transaction

getAllResponseHeaders( )

Returns a list of headers arriving with the response

getresponseHeader( )

Returns a value of a specific header name

open( )

Assigns method, URL, and other attributes to a request waiting to be sent

send( )

Sends the previously specified request over the network

setRequestHeader( )

Sets a header name/value pair for a request waiting to be sent


Every visit of the request object requires both the open( ) and send( ) methods. The open( ) method takes a minimum of two, and as many as five, parameters. The two required parameters are the transaction method (GET or POST as strings) and the destination URL as a string. URLs may be complete or relative. Three optional parameters are: Boolean flag for whether the transmission is executed asynchronously (the default is TRue); an account user name; and account password. The last two parameters are discouraged for client-side use because the values would be retrievable in a source code view.

The parameter to the send( ) method is an empty string when the URL contains a search string conveying data submitted with a GET method or is a simple URL to a server program or XML file (as shown in Example VII-1). For submitting via the POST method (discussed later), the actual data to be submitted to the server (as a string) is the parameter of the send( ) method.

VII.3.4. Request Object Properties

As with request object methods, request object properties have a lowest common denominator set that work across all implementations. Scripts have need for most of these properties after the server data has been retrieved. Table VII-2 lists those properties.

Table VII-2. Lowest common denominator properties of a request object

Property

Description

onreadystatechange

Event property to bind to a function handler that acts during or after the request transaction

readyState

Request object status (0 = uninitialized; 1 = loading; 2 = loaded; 3 = interactive; 4 = complete)

responseText

String version of all data retrieved in last request

responseXML

XML document node (nodeType of 9) if data is an XML data type

status

Numeric status code returned by server (e.g., 200 for success)

statusText

Message string (if any) returned with status integer


All properties of Table VII-2 are read-only except for onreadystatechange. This is the property that lets your script direct the request object to invoke a particular function at every state change of the request object. Assign a function reference to this property after the request object has been opened, but before it has been sent. Object states (readable through the readyState property) are similar to the readyState property of other objects in the Internet Explorer object model. In a typical XMLHttpRequest transaction, the event fires a minimum of three times, once each in the states of loading (1), loaded (2), and complete (4). For larger chunks of data and under the influence of network and server load, the event may fire multiple times in the loading (1) state. In Example VII-1, the event handler function is called with a parameter consisting of a reference to the request object so that the function can receive a reference to the object and inspect its properties while processing the results.

VII.3.5. Handling the Request Response

Among the first jobs of the function handler is to confirm that the request has completed successfully. The following code demonstrates a typical series of statements at the start of a handler function. As noted earlier, the handler function receives as its parameter a reference to the request object instance. Grab that object's request property, through which you can access properties that reveal everything your scripts want to know about the transaction.

 function requestFunctionHandler(req) {     req = req.request;     if (req.readyState == 4 && req.status == 200) {         var xDoc = req.responseXML;         // further processing of document here     } } 

Examine first whether the transaction has completed satisfactorily by looking for a readyState value of 4 and a status value of 200. If those conditions are met, then you can retrieve the XML document as a DOM node through the responseXML property. That value is a reference to the root node of the document. From that reference (the variable xDoc in the example above) you can use W3C DOM node methods to obtain, for example, an array of all elements with a particular tag name (e.g., all elements coded with the <record> tag). After that, you're on your way to read XML element content and plug that data into placeholders in your HTML document, as described in more detail in Online Section IV.

Example VII-2 is a revised version of Example IV-10, using the object-based and reusable request object from Example VII-1 to load data alternately between two separate XML sources. Once the data is retrieved, it is parsed and inserted into an HTML table. This revision also uses the eventManager.js library from Online Section VI to handle event binding to the interactive buttons. Notice that the XML source URLs are passed to the request object via a call to one of the custom-defined methods, loadXMLDoc( ), while the drawTable( ) function contains knowledge about the tbody element it modifies.

Example VII-2. Object-based XMLHttpRequest application

 <html> <head> <title>Blending External XML Data</title> <style type="text/css"> body {background-color: #ffffff} table {border-spacing: 0} td {border: 2px groove black; padding: 7px} th {border: 2px groove black; padding: 7px} .ctr {text-align: center} </style> <script type="text/javascript" src="/books/2/570/1/html/2/eventsManager.js"></script> <script type="text/javascript"> // Draw table from parse XML document tree data function drawTable(req) {     req = req.request;     tbodyID = "bowlData";     if (req.readyState == 4 && req.status == 200) {         var tr, td, i, j, oneRecord;         var tbody = document.getElementById(tbodyID);         clearTable(tbody)         var xDoc = req.responseXML;         if (!xDoc || !xDoc.childNodes.length) {             alert("This example must be loaded from a web " +             "server for Internet Explorer.");             return;         }         // node tree         var data = xDoc.getElementsByTagName("season")[0];         // for td class attributes         var classes = ["ctr","","","","ctr"];         for (i = 0; i < data.childNodes.length; i++) {             // use only 1st level element nodes             if (data.childNodes[i].nodeType == 1) {                 // one bowl record                 oneRecord = data.childNodes[i];                 tr = tbody.insertRow(tbody.rows.length);                 td = tr.insertCell(tr.cells.length);                 td.setAttribute("class",classes[tr.cells.length-1]);                 td.appendChild(document.createTextNode(                    oneRecord.getElementsByTagName("number")[0].firstChild.nodeValue));                 td = tr.insertCell(tr.cells.length);                 td.setAttribute("class",classes[tr.cells.length-1]);                 td.appendChild(document.createTextNode(                    oneRecord.getElementsByTagName("year")[0].firstChild.nodeValue));                 td = tr.insertCell(tr.cells.length);                 td.setAttribute("class",classes[tr.cells.length-1]);                 td.appendChild(document.createTextNode(                    oneRecord.getElementsByTagName("winner")[0].firstChild.nodeValue));                 td = tr.insertCell(tr.cells.length);                 td.setAttribute("class",classes[tr.cells.length-1]);                 td.appendChild(document.createTextNode(                     oneRecord.getElementsByTagName("loser")[0].firstChild.nodeValue));                 td = tr.insertCell(tr.cells.length);                 td.setAttribute("class",classes[tr.cells.length-1]);                 td.appendChild(document.createTextNode(                      oneRecord.getElementsByTagName("winscore")[0].firstChild. nodeValue +                      " - " +                      oneRecord.getElementsByTagName("losscore")[0].firstChild. nodeValue));             }         }     }     // prepare for replacement with data from another source     function clearTable(tbody) {         while (tbody.rows.length > 0) {             tbody.deleteRow(0);         }     } } // constructor function for an XML request object; function XMLDoc( ) {     var me = this;     var req = null;     // branch for native XMLHttpRequest object     if (window.XMLHttpRequest) {         try {             req = new XMLHttpRequest( );         } catch(e) {             req = null;         }     // branch for IE/Windows ActiveX version     } else if (window.ActiveXObject) {         try {             req = new ActiveXObject("Msxml2.XMLHTTP");         } catch(e) {             try {                req = new ActiveXObject("Microsoft.XMLHTTP");             } catch(e) {                req = null;             }         }     }     // preserve reference to request object for later     this.request = req;     // "public" method to be invoked whenever     this.loadXMLDoc = function(url, loadHandler) {         if (this.request) {             this.request.open("GET", url, true);             this.request.onreadystatechange = function ( ) {loadHandler(me)};             this.request.setRequestHeader("Content-Type", "text/xml");             this.request.send("");         }     }; } // create request object instances var request1 = new XMLDoc( ); // assign event handlers to two buttons, each tied to a different XML source function setHandlers( ) {     if (document.getElementById) {         addEvent(document.getElementById("btn1"), "click",             function( ) {request1.loadXMLDoc("superBowls.xml", drawTable);}, false);         addEvent(document.getElementById("btn2"), "click",             function( ) {request1.loadXMLDoc("superBowls2.xml", drawTable);}, false);     } } addOnLoadEvent(setHandlers); </script> </head> <body"> <h1>Super Bowl Games</h1> <hr> <form> <input  type="button" value="First Set"> <input  type="button" value="Second Set"> </form> <table > <thead> <tr><th>Bowl</th>     <th>Year</th>     <th>Winner</th>     <th>Loser</th>     <th>Score (Win - Lose)</th> </tr> </thead> <tbody ></tbody> </table> </body> </html> 




Dynamic HTML. The Definitive Reference
Dynamic HTML: The Definitive Reference
ISBN: 0596527403
EAN: 2147483647
Year: 2004
Pages: 120
Authors: Danny Goodman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net