Native Javascript Ninjutsu: AJAX with XHR

Ninja kanji

Sensei Says (dark ages, to dojos, to disciplines)

Javascript used to be a dark and ancient art, looked down upon by many web developers as a dishonorable – even malicious – ‘copy and paste’ language. Macromedia’s Shockwave – which later became Macromedia Flash, which even later became Adobe Flash – pushed audio, video, and interactive motion graphics onto the web in a cross-browser compatible format that all but decimated the need and appeal for Javascript. What little Javascript community there was began to seriously dwindle and die out.

And then the frameworks came to rise: Dojo, Yahoo! UI Library, Google Web Toolkit, jQuery, Prototype, MooTools, and many more. With these powerful armies by its side, the Javascript community quickly grew and regained its honor, competing heavily with the fluid animation and complex, real-time interactivity that Flash had delivered for years.

Javascript now seems to be a strong, healthy, and widely accepted language, frequently used and relied upon by web developers across the land. Yet how many of today’s programmers can write pure native Javascript without the aid of a framework? How many can perform AJAX requests without a framework? And most importantly, how many can craft fully cross-browser compatible code without a framework? In order to not become dependent on the frameworks – and thus risk sliding backwards into the dark ages – we must maintain a wide variety of practices: these are the native Javascript disciplines.

Discipline 1: Shurikenjutsu (throwing weapons techniques)

Recently I’ve embarked on some missions from my bosses which required me to perform AJAX requests in situations where it would be impossible (and highly inappropriate) to rely on a JS framework. Without a JS framework at your command you must do your AJAX requests the old school way: using the XMLHttpRequest (XHR) object. Creating the XHR object is the same in all modern browsers:

xmlhttp = new XMLHttpRequest();

In older browsers (IE6 and lower) the XHR object isn’t supported and you must us an ActiveX object:

xmlhttp = new ActiveXObject( 'Microsoft.XMLHTTP' );

The english Wikipedia article for the XHR API suggests that Microsoft.XMLHTTP ActiveX object is equivalent to the Msxml2.XMLHTTP.3.0 ActiveX object and further suggests that the XHR object be created as follows:

if ( typeof XMLHttpRequest == 'undefined' )
{
    XMLHttpRequest = function ()
    {
        try
        {
            return new ActiveXObject( 'Msxml2.XMLHTTP.6.0' );
        }
        catch ( e ) {}

        try
        {
            return new ActiveXObject( 'Msxml2.XMLHTTP.3.0' );
        }
        catch ( e ) {}

        try
        {
            return new ActiveXObject( 'Msxml2.XMLHTTP' );
        }
        catch ( e ) {}

        throw new Error( 'This browser does not support XMLHttpRequest.' );
    };
}

xmlhttp = new XMLHttpRequest();

The above code makes sure that the XMLHttpRequest() exists before you attempt to create an instance of it. This technique also makes sure that your code is as widely cross-browser compatible as possible, by checking for three versions of the Msxml2.XMLHTTP ActiveX object instead of just one (Microsoft.XMLHTTP).

Once you’ve got the XHR object created, it’s time to begin using it’s methods to perform various actions. The XHR object’s open method is used to set the options for an HTTP or HTTPS request and accepts up to five parameters. The first two parameters – a request method and a request URL – are required. The last three parameters – a boolean value to signify whether the request is asynchronous (set to true by default), a username, and a password (if the request URL requires authentication) – are optional. Here is the simplest request you can establish with the XHR object’s open method:

xmlhttp.open( 'GET', 'file-location/file-name.xml' );

Here is a more complex request that uses all the optional parameters in order to perform a non-asynchronous request to a URL that requires authentication:

xmlhttp.open( 'GET', 'protected-file-location/file-name.xml', false, 'username', 'password' );

You can also set the request HTTP headers by using the setRequestHeader method, providing the header name and the header value:

xmlhttp.setRequestHeader( 'Content-Type', 'text/xml' );

Once you’ve set the request options with the open method you can then send the request with the aptly named send method:

xmlhttp.send();

The send method can optionally send data if you set up your request using the POST request method:

xmlhttp.open( 'POST', 'file-location/file-name.php' );
xmlhttp.send( 'post-data' );

Finally, in order to retrieve the response you must access one of two properties, responseXML or responseText. If the returned data is sent with an HTTP Content-Type header set to text/xml then the response will be accesible via both responseXML and responseText, though for returned data with a Content-Type of text/plain or text/html the response data will only be accessible via the responseText property. If you’re not sure which content type will be returned, you can simply check for the existance of responseXML before relying on responseText:

if ( xmlhttp.responseXML )
{
    xmlresponse = xmlhttp.responseXML;
}
else if ( xmlhttp.responseText )
{
    xmlresponse = xmlhttp.responseText;
}

Beware of accessing these properties before the response is returned! If you’re using the asynchronous request method you should wrap the response code in the onreadystatechange event listener:

xmlhttp.onreadystatechange = function ()
{
    if ( xmlhttp.readyState == 4 && xmlhttp.status == 200 )
    {
        if ( xmlhttp.responseXML )
        {
            xmlresponse = xmlhttp.responseXML;
        }
        else if ( xmlhttp.responseText )
        {
            xmlresponse = xmlhttp.responseText;
        }
    }
}

The above code assigns a function to the onreadystatechange event listener that checks the readyState and status properties of the XHR object to make sure the URL was found (200) and the response data has finished loading (4). The reason we do this check is because the onreadystatechange event is fired every time the state changes. Here’s the full list of integer values for the readyState property and what they all mean:

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

The status property itself is simply the status code from the browser (read more about status codes in HTTP). There is also a statusText property that would show the text which accompanies the status code, if you should ever need to show it to your user.

Now here’s the whole thing put together:

if ( typeof XMLHttpRequest == 'undefined' )
{
    XMLHttpRequest = function ()
    {
        try
        {
            return new ActiveXObject( 'Msxml2.XMLHTTP.6.0' );
        }
        catch ( e ) {}

        try
        {
            return new ActiveXObject( 'Msxml2.XMLHTTP.3.0' );
        }
        catch ( e ) {}

        try
        {
            return new ActiveXObject( 'Msxml2.XMLHTTP' );
        }
        catch ( e ) {}

        throw new Error( 'This browser does not support XMLHttpRequest.' );
    };
}

xmlhttp = new XMLHttpRequest();

xmlhttp.open( 'GET', 'file-location/file-name.xml' );

xmlhttp.send();

xmlhttp.onreadystatechange = function ()
{
    if ( xmlhttp.readyState == 4 && xmlhttp.status == 200 )
    {
        if ( xmlhttp.responseXML )
        {
            xmlresponse = xmlhttp.responseXML;
        }
        else if ( xmlhttp.responseText )
        {
            xmlresponse = xmlhttp.responseText;
        }
    }
}

Keep in mind that the XHR object restricts you from making cross-domain, cross-protocol, or cross-port requests. Once you’ve successfully retrieved your response data you can also access the HTTP response headers with the getResponseHeader and getAllResponseHeaders methods, the former of which accepts the name of the header as its single (required) parameter:

xmlresponseheader = xmlhttp.getResponseHeader( 'Content-Type' );
xmlresponseheaders = xmlhttp.getAllResponseHeaders();

Now go out there, practice your native Javascript AJAX, and stay tuned for next week’s discipline: Dynamic Scripts with PHP.

Resources

W3C’s XMLHttpRequest recommendation
W3School’s XML Javascript tutorial
Wikipedia’s XMLHttpRequest article
Apple Developer’s XMLHttpRequest article

Are you smart? Innovative? Driven? If you’re interested in working on challenging projects in one of the world’s most fast-paced industries, why not check out the openings on our Careers page?