Thursday, May 21, 2009

Creating an XMLHttp Object in PHP AJAX

The first step to using an XMLHttp object is, obviously, to create one. Because Microsoft's implementation is an ActiveX control, you must use the proprietary ActiveXObject class in JavaScript, passing in the XMLHttp control's signature:
var oXmlHttp = new ActiveXObject("Microsoft.XMLHttp");
This line creates the first version of the XMLHttp object (the one shipped with IE 5.0). The problem is that there have been several new versions released with each subsequent release of the MSXML library. Each release brings with it better stability and speed, so you want to make sure you are always using the most recent version available on the user's machine. The signatures are:
1 Microsoft.XMLHttp 2 MSXML2.XMLHttp 3 MSXML2.XMLHttp.3.0 4 MSXML2.XMLHttp.4.0 5 MSXML2.XMLHttp.5.0
Unfortunately, the only way to determine the best version to use is to try to create each one. Because this is an ActiveX control, any failure to create an object will throw an error, which means that you must enclose each attempt within a try...catch block. The end result is a function such as this:
function createXMLHttp() { var aVersions = [ "MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp" ]; for (var i = 0; i < oxmlhttp =" new">
The createXMLHttp() function stores an array of XMLHttp signatures, with the most recent one first. It iterates through this array and tries to create an XMLHttp object with each signature. If the creation fails, the catch statement prevents a JavaScript error from stopping execution; then the next signature is attempted. When an object is created, it is returned. If the function completes without creating an XMLHttp object, an error is thrown indicating that the creation failed. Fortunately, creating an XMLHttp object is much easier in other browsers. Mozilla Firefox, Safari, and Opera all use the same code:
var oXmlHttp = new XMLHttpRequest();
Naturally, it helps to have a cross-browser way of creating XMLHttp objects. You can create such a function by altering the createXMLHttp() function defined previously: function createXMLHttp() { if (typeof XMLHttpRequest != "undefined") { return new XMLHttpRequest(); } else if (window.ActiveXObject) { var aVersions = [ "MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0","MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp","Microsoft.XMLHttp" ]; for (var i = 0; i < oxmlhttp =" new">
The other option for creating cross-browser XMLHttp objects is to use a library that already has cross-browser code written. The zXml library, written by two of your authors, is one such library and is available for download at www.nczonline.net/downloads/. This library defines a single function for the creation of XMLHttp objects: var oXmlHttp = zXmlHttp.createRequest(); The createRequest() function, and the zXml library itself, will be used throughout this book to aid in cross-browser handling of Ajax technologies. Using XMLHttp After you have created an XMLHttp object, you are ready to start making HTTP requests from JavaScript. The first step is to call the open() method, which initializes the object. This method accepts the following three arguments:
Request Type: A string indicating the request type to be made — typically, GET or POST (these are the only ones currently supported by all browsers). URL: A string indicating the URL to send the request to. Async: A Boolean value indicating whether the request should be made asynchronously.
The last argument, async, is very important because it controls how JavaScript executes the request. When set to true, the request is sent asynchronously, and JavaScript code execution continues without waiting for the response; you must use an event handler to watch for the response to the request. If async is set to false, the request is sent synchronously, and JavaScript waits for a response from the server before continuing code execution. That means if the response takes a long time, the user cannot interact with the browser until the response has completed. For this reason, best practices around the development of Ajax applications favor the use of asynchronous requests for routine data retrieval, with synchronous requests reserved for short messages sent to and from the server. To make an asynchronous GET request to info.txt, you would start by doing this:
var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true);
Note that the case of the first argument, the request type, is irrelevant even though technically request types are defined as all uppercase. Next, you need to define an onreadystatechange event handler. The XMLHttp object has a property called readyState that changes as the request goes through and the response is received. There are five possible values for readyState:
0 (Uninitialized): The object has been created but the open() method hasn't been called. 1 (Loading): The open() method has been called but the request hasn't been sent. 2 (Loaded): The request has been sent. 3 (Interactive). A partial response has been received. 4 (Complete): All data has been received and the connection has been closed.
Every time the readyState property changes from one value to another, the readystatechange event fires and the onreadystatechange event handler is called. Because of differences in browser implementations, the only reliable readyState values for cross-browser development are 0, 1, and 4. In most cases, however, you will check only for 4 to see when the request has returned: var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { alert("Got response."); } }; The last step is to call the send() method, which actually sends the request. This method accepts a single argument, which is a string for the request body. If the request doesn't require a body (remember, a GET request doesn't), you must pass in null:
var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { alert("Got response."); } }; oXmlHttp.send(null); That's it! The request has been sent and when the response is received, an alert will be displayed. But just showing a message that the request has been received isn't very useful. The true power of XMLHttp is that you have access to the returned data, the response status, and the response headers.
To retrieve the data returned from the request, you can use the responseText or responseXML properties. The responseText property returns a string containing the response body, whereas the responseXML property is an XML document object used only if the data returned has a content type of text/xml. (XML documents are discussed in Chapter 4.) So, to get the text contained in info.txt, the call would be as follows:
var sData = oXmlHttp.responseText;
Note that this will return the text in info.txt only if the file was found and no errors occurred. If, for example, info.txt didn't exist, then the responseText would contain the server's 404 message. Fortunately, there is a way to determine if any errors occurred. The status property contains the HTTP status code sent in the response, and statusText contains the text description of the status (such as "OK" or "Not Found"). Using these two properties, you can make sure the data you've received is actually the data you want or tell the user why the data wasn't retrieved: if (oXmlHttp.status == 200) { alert("Data returned is: "+ oXmlHttp.responseText; } else { alert("An error occurred: "+ oXmlHttp.statusText; } Generally, you should always ensure that the status of a response is 200, indicating that the request was completely successful. The readyState property is set to 4 even if a server error occurred, so just checking that is not enough. In this example, the responseText property is shown only if the status is 200; otherwise, the error message is displayed. Important The statusText property isn't implemented in Opera and sometimes returns an inaccurate description in other browsers. You should never rely on statusText alone to determine if an error occurred. As mentioned previously, it's also possible to access the response headers. You can retrieve a specific header value using the getResponseHeader() method and passing in the name of the header that you want to retrieve. One of the most useful response headers is Content-Type, which tells you the type of data being sent:
var sContentType = oXmlHttp.getResponseHeader("Content-Type"); if (sContentType == "text/xml") { alert("XML content received."); } else if (sContentType == "text/plain") { alert("Plain text content received."); } else { alert("Unexpected content received."); } This code snippet checks the content type of the response and displays an alert indicating the type of data returned. Typically, you will receive only XML data (content type of text/xml) or plain text (content type of text/plain) from the server, because these content types are the easiest to work with using JavaScript. If you'd prefer to see all headers returned from the server, you can use the getAllResponseHeaders() method, which simply returns a string containing all of the headers. Each heading in the string is separated by either a new line character (\n in JavaScript) or a combination of the carriage return and new line (\r\n in JavaScript), so you can deal with individual headers as follows:
var sHeaders = oXmlHttp.getAllResponseHeaders(); var aHeaders = sHeaders.split(/\r?\n/); for (var i=0; i <>
It's also possible to set headers on the request before it's sent out. You may want to indicate the content type of data that you'll be sending, or you may just want to send along some extra data that the server may need to deal with the request. To do so, use the setRequestHeader() method before calling send():
var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", true); oXmlHttp.onreadystatechange = function () { if (oXmlHttp.readyState == 4) { alert("Got response."); } }; oXmlHttp.setRequestHeader("myheader", "myvalue"); oXmlHttp.send(null); In this code, a header named myheader is added to the request before it's sent out. The header will be added to the default headers as myheader: myvalue. Up to this point, you've been dealing with asynchronous requests, which are preferable in most situations. Sending synchronous requests means that you don't need to assign theonreadystatechange event handler because the response will have been received by the time the send() method returns. This makes it possible to do something like this: var oXmlHttp = zXmlHttp.createRequest(); oXmlHttp.open("get", "info.txt", false); oXmlHttp.send(null); if (oXmlHttp.status == 200) { alert("Data returned is: "+ oXmlHttp.responseText; } else { alert("An error occurred: "+ oXmlHttp.statusText; }
Sending the request synchronously (setting the third argument of open() to false) enables you to start evaluating the response immediately after the call to send(). This can be useful if you want the user interaction to wait for a response or if you're expecting to receive only a very small amount of data (for example, less than 1K). In the case of average or larger amounts of data, it's best to use an asynchronous call.

No comments: