Chapter 32. Using XMLHttpRequest Objects

In Lesson 31 you learned that there are a variety of ways to incorporate Ajax into a web application. The older technique of using hidden frames can still be found in many modern web applications, but the most popular way to add Ajax functionality to an application is by using the XMLHttpRequest (XHR) object. Despite its name, you can retrieve more than XML with XHR. In fact, it's more commonly used to retrieve plain text.

The XHR object originated as a component, called XmlHttp, in Microsoft's MSXML library. The component was first released with Internet Explorer (IE) 5. XmlHttp allowed developers to make HTTP requests, using JavaScript or VBScript, to retrieve XML data. Microsoft updated the MSXML library with every major version of IE, and each new version included an improved XmlHttp component.

As the popularity of Microsoft's XmlHttp grew, the developers behind Mozilla built their own version (calling it XMLHttpRequest) for Firefox. Even though it had a different name, the Mozilla developers copied the majority of the properties and methods of Microsoft's XmlHttp, making cross-browser use easier. Soon after, the developers of Opera and Safari copied Mozilla's XHR object, making transparent HTTP requests through pure JavaScript possible in all modern browsers.

CREATING XHR OBJECTS

There are essentially two types of browsers when it comes to XHR support. The first supports the native XHR object—meaning that XHR is built into the browser. The second does not natively support XHR but can use the XmlHttp component found in the MSXML library.

Let's first look at the browsers that natively support XHR and learn how to make XHR objects.

Browsers with Native Support

All modern browsers have native XHR support. Creating an XHR object in these browsers is incredibly easy. Simply call the constructor using the new operator, like this:

var xhr = new XMLHttpRequest();

It's that simple, and it works in the following browsers:

  • IE7+

  • Firefox 1+

  • Opera 8+

  • Safari 1.2+

  • Chrome 1+

Browsers that Support XmlHttp

The MSXML library is an ActiveX library, so only Internet Explorer supports XmlHttp. So this section focuses on IE5, IE5.5, and IE6. These browsers are quite old, and Microsoft does not support them anymore. Still, despite Microsoft's efforts, IE6 holds an estimated 10% market share at the time of this writing. That's a huge number.

Note

Even though IE7+ supports the XHR object and constructor, it actually creates an XmlHttp object from the MSXML library. So while IE7+ does support the XMLHttpRequest identifier, the actual object is not native to the browser.

Because XmlHttp is an ActiveX component, instantiating an XmlHttp object requires the creation of an ActiveX object. Creating ActiveX objects consists of calling the ActiveXObject constructor and passing it a string containing the ActiveX component you want to create, like this:

var xhr = new ActiveXObject("Microsoft.XMLHttp");

This code creates the first version of Microsoft's XmlHttp. There are actually several versions, but Microsoft recommends using one of the following:

  • MSXML2.XmlHttp.6.0

  • MSXML2.XmlHttp.3.0

You want to use the latest version possible because it will contain bug fixes and performance gains over previous versions. Unfortunately it's impossible to know if everyone has the latest version installed, but you can write code that determines what version users do have, in order to create appropriate XmlHttp objects.

The idea behind this code is to loop through the two recommended version names and attempt to create an XmlHttp object with each—starting with version 6 because it's the latest version. If a user's computer does not have version 6 installed, the browser throws an error when you attempt to create a version 6 XmlHttp object. You can catch that error and continue the loop to attempt the creation of a version 3 object. The following code demonstrates this process:

var versions = [ "MSXML2.XmlHttp.6.0",
    "MSXML2.XmlHttp.3.0" ];

for (var i = 0; i < versions.length; i++) {
    try {
       var xhr = new ActiveXObject(versions[i]);
    } catch (error) {
        // do nothing
    }
}

The first statement in this code defines an array called versions, and each of its elements is a version string for instantiating an XmlHttp ActiveX object. Next, a for loop iterates over the array, and inside the loop, you see a try...catch block. Remember, the try block "tries" to execute code, and the catch block catches an error if one is thrown from the execution of code in the try block.

This portion of the process is crucial, as the code attempts to create XmlHttp ActiveX objects based on the current version number. The line that can fail is bolded in the previous code. If the code tries to create a version 6 XmlHttp object and fails, the catch block catches the error and does nothing. The loop then iterates and the code tries to create a version 3 object. When an XmlHttp object is successfully created, the xhr variable contains an XmlHttp object. Otherwise it contains the value undefined.

Later in this lesson, you write a function that combines both approaches to creating XHR and XmlHttp objects. But before doing that, let's get started looking at some of these objects' features.

Note

For the sake of simplicity, both XMLHttpRequest and XmlHttp objects will be referred to as XHR objects unless a distinction between the two needs to be made.

USING XHR OBJECTS

After creating an XHR object you can begin to make HTTP requests with it. The first step is to initialize the request by calling the open() method. It accepts the following three arguments:

  • Request method: A string indicating what type of request to make. Valid values are GET and POST, and you'll learn about these in Lessons 33 and 34, respectively.

  • URL: A string containing the URL to send the request to

  • Asynchronous mode: A Boolean value determining whether to use asynchronous (true) or synchronous (false) mode

Calling the open() method looks something like this:

xhr.open("GET", "info.txt", true);

This code initializes a GET request to be sent to retrieve a text file called info.txt using asynchronous mode. The choice between asynchronous and synchronous mode is an important one because it determines how the XHR affects your application.

Asynchronous mode means that the XHR object sends a request, waits for a response, and processes the response without interrupting the loading of the rest of the page or the execution of other scripts. Synchronous mode means the browser stops and waits for the XHR object to send the request, receive a response, and process the response before continuing to load the page or execute other scripts. The mode you use can drastically alter the performance of your application.

Synchronous requests are simpler than asynchronous requests. With synchronous requests you initialize the request, send it, and then process the server's response. The problem is that synchronous requests cause the browser to stop everything until it receives a response. In fact, users can get the feeling that something is wrong with the page, and you don't want that. Asynchronous mode is a better choice even though it requires extra code and complexity.

When you make an asynchronous request the XHR object goes through five different stages called ready states, which have numerical values. They are:

  • 0: The object has been created but not initialized.

  • 1: The object has been initialized but the request has not been sent.

  • 2: The request has been sent.

  • 3: A response has been received from the HTTP server.

  • 4: The requested data has been fully received.

All XHR objects have a readyState property, and it contains one of the aforementioned numerical values. When the readyState property's value changes, the XHR's readystatechange event fires and calls the onreadystatechange event handler. The onreadystatechange event handler is usually defined like this:

xhr.open("GET", "info.txt", true);

xhr.onreadystatechange = function() {
    // more code here
};

Next you need to determine whether the requested data is completely available, because you can't work with data if you don't have it. The ready state you're primarily interested in is the fifth state, which has a value of 4. In order to ensure you have the requested data, check the readyState property's value inside the onreadystatechange event handler, like this:

xhr.open("GET", "info.txt", true);

xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
        alert("We got a response");
}
};

xhr.send(null);

The onreadystatechange event handler fires every time the value of readyState changes; therefore, the function handling the event executes four times. In this code, an if statement checks to see whether readyState is 4, meaning the request is complete. If it is, an alert box displays a message stating that a response was received from the server. The final line of this code sends the request with the send() method. You'll learn more about send() in Lessons 33 and 34.

Even when an XHR's ready state is 4 (fully complete), it is not guaranteed that you have the data you wanted. An error may have occurred.

DETERMINING THE SUCCESS OF A REQUEST

It's entirely possible that the resource you requested doesn't exist, or that the server-side application processing your request had an internal error. There's a whole host of issues that could crop up and cause the server to not provide you with the data you requested. It doesn't matter if you use asynchronous or synchronous mode, errors happen, and you need some way to identify them. Fortunately, determining when these errors happen is rather simple: Look at the response status code.

When sending a response to a client, a server must include a status code that indicates the success or failure of a request. These response codes are standardized parts of the HTTP protocol, they are three digits long, and they fall into five different categories:

  • 1xx: Informational

  • 2xx: Success

  • 3xx: Redirection

  • 4xx: Client error

  • 5xx: Server error

You may never actually see the status codes, but they exist behind the scenes and every HTTP-enabled client can understand them. For example, when users go to Google's homepage at http://www.google.com, Google's webserver sends a status code of 200 as part of its response to the browser. This code is the standard response for successful HTTP requests. It literally means "OK." However, if users point their browsers to http://www.google.com/javascript/, Google's webserver responds with a status code of 404. This means that the specified resource could not be found on the server (for example, it doesn't exist).

You can determine whether or not your request was successful by using the XHR object's status property, which contains a three-digit HTTP response code, and comparing it to an HTTP response code, like this:

if (xhr.status === 200) {
    alert("Everything is OK.");
}

This code uses the status property to determine whether the request completed successfully by comparing the status property to the status code of 200. But this check is actually inadequate if you want to determine the success of a request. The 200 status code works in most cases, but the status codes from 200 to 299 are considered successful status codes. Additionally, status code 304 is considered a success because it signifies that the requested resource has not changed since it was last requested. So, in order to adequately determine the success of a request, your code should check if the response status is within the 200s or is equal to 304, like this:

var status = xhr.status;

if ((status >= 200 && status < 300) || status === 304 ) {
    alert("Everything is OK.");
} else {
    alert("Something went wrong.");
}

This code first assigns the XHR's status property to a variable called status. It then checks if status is within the range of 200 through 299 or equal to 304. This way, all successful status codes result in the message, "Everything is OK." If the request results in any other status code, it is considered an error and the user receives the message "Something went wrong."

Note

Status code checking should be done for both asynchronous and synchronous requests.

SECURITY

There is no doubt that XHR adds a tremendous amount of power to your applications, but this power does not come without limitations. XHR is a JavaScript component, and as such, it is bound to the rules and limitations of the language—notably JavaScript's security.

In order to thwart malicious coders, JavaScript cannot access scripts or documents from a different origin—only scripts or documents from the same origin. The same-origin policy dictates that two pages (or scripts) are from the same origin only if the protocol, port, and host are the same. Consider the following two URLs:

http://www.wrox.com
http://www.wrox.com/WileyCDA/

These two URLs are of the same origin. They share the same protocol (HTTP), the same host (www.wrox.com), and the same port (80 is default). Because these are of the same origin, JavaScript on one page can access the other. Now look at the next two URLs:

http://www.wrox.com
https://www.wrox.com/WileyCDA/

These two URLs are not of the same origin because their protocols and ports don't match (the default HTTPS port is 443). So JavaScript on one of these pages cannot access the other page.

Why is this important? It's important because Ajax relies on JavaScript. The same-origin policy prohibits any JavaScript component, XHR included, to access any resource from another origin. Developers get around this issue by writing a server-side service to act as a proxy to retrieve the resource from another origin to return to the XHR object.

You've learned about a lot of the components of XHR objects in this lesson, so let's combine them all to give you a clearer picture of how they fit together.

TRY IT

In this lesson, you are introduced to several basic pieces of the XHR object: how to create one, how to determine the object's ready state for asynchronous mode, and how to determine whether a request was successful. You combine all these pieces to create a foundation you use in later lessons.

Lesson Requirements

For this lesson, you need a text editor; any plain text editor will do. For Microsoft Windows users, Notepad is available by default on your system or you can download Microsoft's free Visual Web Developer Express (www.microsoft.com/express/web/) or Web Matrix (www.asp.net/webmatrix/). Mac OS X users can use TextMate, which comes as part of OS X, or download a trial for Coda (www.panic.com/coda/). Linux users can use the built-in VIM.

You also need a modern web browser. Choose any of the following:

  • Internet Explorer 8+

  • Google Chrome

  • Firefox 3.5+

  • Apple Safari 4+

  • Opera 10+

Additionally, you need webserver software installed on your computer. Refer to Lesson 31 on the DVD for installation instructions. Create a subfolder called Lesson32 in the root directory of your webserver. Store the files you create in this lesson in the Lesson32 folder.

Step-by-Step

  1. Write a function to create an XHR object for all browsers. Call the function createXHR(), and have it return an XHR object. Its code follows:

    function createXHR() {
        if (typeof XMLHttpRequest !== "undefined") {
            return new XMLHttpRequest();
        } else {
            var versions = [ "MSXML2.XmlHttp.6.0",
    "MSXML2.XmlHttp.3.0" ];
    
            for (var i = 0; i < versions.length; i++) {
                try {
                    var xhr = new ActiveXObject(versions[i]);
                    return xhr;
                } catch (error) {
                    // do nothing
                }
            }
        }
    
        alert("Your browser does not support XmlHttp");
    
        return null;
    }

    This code first uses feature detection to determine whether the browser supports the XMLHttpRequest identifier, and returns an XHR object if it does. The check for XHR here is important because IE7+ can use either the XMLHttpRequest constructor or the ActiveXObject constructor to create an XmlHttp object, and the XHR constructor in IE7+ is guaranteed to create the latest version of XmlHttp available. So the ActiveX code is used only if the browser doesn't support the XMLHttpRequest identifier.

  2. Now call the createXHR() function and assign the resulting XHR object to a variable called xhr. Then assign a function to the onreadystatechange event handler, as shown in the following code:

    var xhr = createXHR();
    
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
    
        }
    };

    Inside the onreadystatechange event handler is an if statement to determine whether the ready state is 4—meaning that the XHR object received the entire response from the server.

  3. Now ensure that the request was successful by using the status property. Place the status-checking code within the if statement checking the ready state. The new code is bold:

    var xhr = createXHR();
    
    xhr.onreadystatechange = function() {
        if (xhr.readyState === 4) {
            if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 ) {
                // code for success goes here
            } else {
                alert("Something went wrong.");
            }
        }
    };

    By adding this code, you make the onreadystatechange event handler ready to use and process the response from the server. It checks the XHR object's ready state, and then it checks to make sure a proper response was received from the server.

  4. Save this file as lesson32_example01.js.

To get the sample code files, download Lesson 32 from the book's website at www.wrox.com.

Note

Please select Lesson 32 on the DVD to view the video that accompanies this lesson.

Step-by-Step
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.58.121.8