XMLHttpRequest

XMLHttpRequest() is an object (a constructor function) that allows you to send HTTP requests from JavaScript. Historically, XHR (XMLHttpRequest) was introduced in IE and was implemented as an ActiveX object. Starting with IE7, it's a native browser object, the same way as it's in the other browsers. The common implementation of this object across browsers gave birth to the so-called Ajax applications, where it's no longer necessary to refresh the whole page every time you need new content. With JavaScript, you can make an HTTP request to the server, get the response, and update only a part of the page. This way, you can build much more responsive and desktop-like web pages.

Ajax stands for Asynchronous JavaScript and XML:

  • Asynchronous because, after sending an HTTP request, your code doesn't need to wait for the response; however, it can do other stuff and be notified, through an event, when the response arrives.
  • JavaScript because it's obvious that XHR objects are created with JavaScript.
  • XML because initially developers were making HTTP requests for XML documents and were using the data contained in them to update the page. This is no longer a common practice, though, as you can request data in plain text, in the much more convenient JSON format, or simply as HTML ready to be inserted into the page.

There are two steps to using the XMLHttpRequest object, which are as follows:

  • Send the request: This includes creating an XMLHttpRequest object and attaching an event listener
  • Process the response: This happens when your event listener gets notified that the response has arrived, and your code gets busy doing something amazing with the response

Sending the request

In order to create an object, you will simply use the following code (let's deal with browser inconsistencies in just a bit):

    var xhr = new XMLHttpRequest(); 

The next thing is to attach an event listener to the readystatechange event fired by the object:

    xhr.onreadystatechange = myCallback; 

Then, you will need to call the open() method, as follows:

    xhr.open('GET', 'somefile.txt', true); 

The first parameter specifies the type of HTTP request, such as GET, POST, HEAD, and so on. GET and POST are the most common ones. Use GET when you don't need to send much data with the request and your request doesn't modify (write) data on the server, otherwise, use POST. The second parameter is the URL you are requesting. In this example, it's the text file somefile.txt located in the same directory as the page. The last parameter is a Boolean specifying whether the request is asynchronous (true, always prefer this) or not (false, blocks all the JavaScript execution and waits until the response arrives).

The last step is to fire off the request, which is done as follows:

    xhr.send(''), 

The send() method accepts any data you want to send with the request. For GET requests, this is an empty string because the data is in the URL. For POST request, it's a query string in the key=value&key2=value2 form.

At this point, the request is sent and your code and the user can move on to other tasks. The callback function, myCallback, will be invoked when the response comes back from the server.

Processing the response

A listener is attached to the readystatechange event. So, what exactly is the ready state and how does it change?

There is a property of the XHR object called readyState. Every time it changes, the readystatechange event fires. The possible values of the readyState property are as follows:

  • 0-uninitialized
  • 1-loading
  • 2-loaded
  • 3-interactive
  • 4-complete

When readyState gets the value of 4, it means the response is back and ready to be processed. In myCallback, after you make sure readyState is 4, the other thing to check is the status code of the HTTP request. You might have requested a non-existing URL, for example, and got a 404 (File not found) status code. The interesting code is the 200 (OK) code, so myCallback should check for this value. The status code is available in the status property of the XHR object.

Once xhr.readyState is 4 and xhr.status is 200, you can access the contents of the requested URL using the xhr.responseText property. Let's see how myCallback can be implemented to simply alert() the contents of the requested URL:

    function myCallback() { 
 
      if (xhr.readyState < 4) { 
        return; // not ready yet 
      } 
  
      if (xhr.status !== 200) { 
        alert('Error!'), // the HTTP status code is not OK 
        return; 
      } 
 
      //  all is fine, do the work 
      alert(xhr.responseText); 
    } 

Once you've received the new content you requested, you can add it to the page, use it for some calculations, or for any other purpose you find suitable.

Overall, this two-step process (send request and process response) is the core of the whole XHR/Ajax functionality. Now that you know the basics, you can move on to building the next Gmail. Oh yes, let's take a look at some minor browser inconsistencies.

Creating XMLHttpRequest objects in IE prior to Version 7

In Internet Explorer, prior to version 7, the XMLHttpRequest object was an ActiveX object, so creating an XHR instance is a little different. It goes as follows:

    var xhr = new ActiveXObject('MSXML2.XMLHTTP.3.0'), 

MSXML2.XMLHTTP.3.0 is the identifier of the object you want to create. There are several versions of the XMLHttpRequest object, and if your page visitor doesn't have the latest one installed, you can try two older ones before you give up.

For a fully-cross-browser solution, you should first test to see if the user's browser supports XMLHttpRequest as a native object, and if not, try the IE way. Therefore, the whole process of creating an XHR instance could be like the following:

    var ids = ['MSXML2.XMLHTTP.3.0', 
           'MSXML2.XMLHTTP', 
           'Microsoft.XMLHTTP']; 
            
    var xhr; 
    if (XMLHttpRequest) { 
      xhr = new XMLHttpRequest(); 
    } else { 
      // IE: try to find an ActiveX object to use 
      for (var i = 0; i < ids.length; i++) { 
        try { 
          xhr = new ActiveXObject(ids[i]); 
          break; 
        } catch (e) {} 
      } 
    } 

What is this doing? The ids array contains a list of ActiveX program IDs to try. The xhr variable points to the new XHR object. The code first checks to see if XMLHttpRequest exists. If so, this means that the browser supports XMLHttpRequest() natively, so the browser is relatively modern. If it is not, the code loops through ids trying to create an object. The catch(e) block quietly ignores failures and the loop continues. As soon as an xhr object is created, you break out of the loop.

As you can see, this is quite a bit of code, so it's best to abstract it into a function. Actually, one of the exercises at the end of the chapter prompts you to create your own Ajax utility.

A is for Asynchronous

Now you know how to create an XHR object, give it a URL and handle the response to the request. What happens when you send two requests asynchronously? What if the response to the second request comes before the first?

In the preceding example, the XHR object was global and myCallback was relying on the presence of this global object in order to access its readyState, status, and responseText properties. Another way, which prevents you from relying on global variables, is to wrap the callback in a closure. Let's see how:

    var xhr = new XMLHttpRequest(); 
 
    xhr.onreadystatechange = (function (myxhr) { 
      return function () {  
        myCallback(myxhr);  
      }; 
    }(xhr)); 
 
    xhr.open('GET', 'somefile.txt', true); 
    xhr.send(''), 

In this case, myCallback() receives the XHR object as a parameter and will not go looking for it in the global space. This also means that at the time the response is received, the original xhr might be reused for a second request. The closure keeps pointing to the original object.

X is for XML

Although these days JSON (discussed in the next chapter) is preferred over XML as a data transfer format, XML is still an option. In addition to the responseText property, the XHR objects also have another property called responseXML. When you send an HTTP request for an XML document, responseXML points to an XML DOM document object. To work with this document, you can use all of the core DOM methods discussed previously in this chapter, such as getElementsByTagName(), getElementById(), and so on.

An example

Let's wrap up the different XHR topics with an example. You can visit the page located at http://www.phpied.com/files/jsoop/xhr.html to work on the example yourself.

The main page, xhr.html, is a simple static page that contains nothing but three <div> tags, which are as follows:

    <div id="text">Text will be here</div> 
    <div id="html">HTML will be here</div> 
    <div id="xml">XML will be here</div> 

Using the console, you can write code that requests three files and loads their respective contents into each <div>.

The three files to load are as follows:

  • content.txt: This is a simple text file containing the text I am a text file
  • content.html: This is a file containing HTML code I am <strong>formatted</strong> <em>HTML</em>
  • content.xml: This is an XML file containing the following code:
        <?xml version="1.0" ?> 
        <root> 
            I'm XML data. 
        </root> 
    

All of the files are stored in the same directory as xhr.html.

Note

For security reasons, you can only use the original XMLHttpRequest to request files that are on the same domain. However, modern browsers support XHR2, which lets you make cross-domain requests, provided that the appropriate Access-Control-Allow-Origin HTTP header is in place.

First, let's create a function to abstract the request/response part:

    function request(url, callback) { 
      var xhr = new XMLHttpRequest();  
      xhr.onreadystatechange = (function (myxhr) { 
        return function () { 
          if (myxhr.readyState === 4 && myxhr.status === 200) { 
            callback(myxhr); 
          } 
        }; 
      }(xhr)); 
      xhr.open('GET', url, true); 
      xhr.send(''), 
    } 

This function accepts a URL to request and a callback function to call once the response arrives. Let's call the function three times, once for each file, as follows:

    request( 
      'http://www.phpied.com/files/jsoop/content.txt', 
      function (o) { 
        document.getElementById('text').innerHTML = 
          o.responseText; 
      } 
    ); 
    request( 
      'http://www.phpied.com/files/jsoop/content.html', 
      function (o) { 
        document.getElementById('html').innerHTML = 
          o.responseText; 
      } 
    ); 
    request( 
      'http://www.phpied.com/files/jsoop/content.xml', 
      function (o) { 
        document.getElementById('xml').innerHTML = 
          o.responseXML 
           .getElementsByTagName('root')[0] 
           .firstChild 
           .nodeValue; 
      }   
    ); 

The callback functions are defined inline. The first two are identical. They just replace the HTML of the corresponding <div> with the contents of the requested file. The third one is a little different as it deals with the XML document. First, you will access the XML DOM object as o.responseXML. Then, using getElementsByTagName(), you will get a list of all the <root> tags (there is only one). The firstChild of <root> is a text node and nodeValue is the text contained in it (I'm XML data). Then, just replace the HTML of <div id="xml"> with the new content. The result is shown in the following screenshot:

An example

When working with the XML document, you can also use o.responseXML.documentElement to get to the <root> element instead of o.responseXML.getElementsByTagName('root')[0]. Remember that documentElement gives you the root node of an XML document. The root in HTML documents is always the <html> tag.

..................Content has been hidden....................

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