Chapter 4. AJAX

One of jQuery’s most used features is its suite of AJAX functions. They offer some significant improvements over the native JavaScript AJAX features, as they are a lot easier to use. AJAX is the act of making an HTTP request from JavaScript without having to reload the page; you could think of it as an inline HTTP request. Sometimes, however, it isn’t worth loading the entire jQuery library to send a few requests and nothing else, or it doesn’t provide enough control. It’s also useful to know how jQuery does it, as it can help you when you’re trying to debug your code. jQuery’s $.ajax function is also a massive 379 lines long at the time of writing.

In this chapter, we will cover the basics of AJAX, explore a bit about designing a website to use AJAX, and then AJAXify an example piece of code. This chapter contains a bit of PHP, but PHP knowledge isn’t strictly necessary—it’s not complicated PHP and I will be explaining it along the way.

Sending an AJAX Request

To send an AJAX request in jQuery, we use this very simple syntax:

$.get('/ajax/?foo=bar', function (data) {
        console.log(data); // The response
});

Sending an AJAX request in JavaScript alone is a lot more complicated. To send an AJAX request, we use the XMLHttpRequest object (or ActiveXObject in older versions of Internet Explorer). Here is an example of a basic AJAX request:

if (window.XMLHttpRequest) {
        var req = new XMLHttpRequest();
} else {
        // Internet Explorer
        var req = new ActiveXObject('Microsoft.XMLHTTP');
}

var url = '/ajax/?foo=bar';

req.open('GET', url, true);

req.onreadystatechange = function () {
        if (req.readyState === 4 && req.status === 200) {
                console.log(req.responseText);
        } else if (req.readyState === 4) {
                throw new Error('XHR Request failed: ' + req.status);
        }
};

req.send();

That sends a GET request to /ajax/?foo=bar, and then if the request succeeds, logs the output to the console. If it fails, it throws an error. There are a few things that you should know about this example:

  • The third argument of req.open should always be set to true, as it tells the browser that you want to make the request asynchronous (runs in the background and then calls the callback, as opposed to blocking the page until the request returns). If set to false, it tells the browser that you want a synchronous request. This used to cause a memory leak in Firefox, so Mozilla disabled it; attempting to send a request synchronously now just throws an error. It was also bad practice to use it when it did work.
  • It is safe to use .onreadystatechange instead of adding an event listener, as we know that only one event listener will be added to it and nothing will be overwritten. It isn’t worth the hassle of adding support for both .addEventListener and .attachEvent.
  • req.readyState will always be set to 4, eventually. There is no need to have a setTimeout to throw an error on timeout.
  • When sending GET requests, you should just append any data you want to send to the end of the URL (like you would for a normal HTTP request). We’ll cover POST requests in a bit.

Debugging

For debugging AJAX requests, it is wise to have a simple echo script installed to which you can send data. Mine is written in PHP, but it doesn’t matter what language it is written in:

<?php

echo json_encode(array(
        'method'        => $_SERVER['request_method'],
        'GET'           => $_GET,
        'POST'          => $_POST
));

?>

That sample of code simply sends a JSON-encoded array of information: first, it sends the request method (usually GET or POST, but DELETE and PUT are also fairly common), and then it dumps the GET ($_GET) and POST ($_POST) information. This allows us to see exactly what has been sent. We can use JSON.parse to turn it into a JavaScript object when we receive it at the client side.

JSON (JavaScript Object Notation) is a unified format for sending data with either native support or libraries written for almost every language. It was pioneered by Douglas Crockford, who also wrote JSLint and JSCheck. In JavaScript, we can use JSON.stringify to turn it into JSON, or JSON.parse to turn it back into a real JavaScript object. It and XML are the most commonly used formats for sending data over AJAX requests, although JSON tends to be more popular recently. I prefer JSON, as it offers better support and uses less bandwidth. Most APIs offer both XML and JSON.

Debugging Sent AJAX Requests

Most good browsers have an option in the console (usually accessible by right-clicking) that, when enabled, will display all AJAX requests in the console. It can be helpful for seeing whether a request has actually been sent, and whether the problem is on the client side or server side.

Some browsers will also log the returned data when this option is enabled, which can be helpful, as well.

Sending POST Requests in JavaScript

The syntax to send a POST request is mostly the same as that to send a GET request, with a couple of minor differences. The following code sends the same data to the same URL as this chapter’s first code example, but using POST instead of GET:

if (window.XMLHttpRequest) {
        var req = new XMLHttpRequest();
} else {
        // Internet Explorer
        var req = new ActiveXObject('Microsoft.XMLHTTP');
}
var url = '/ajax/';
var data = 'foo=bar';

req.open('POST', url, true);

req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

req.onreadystatechange = function () {
        if (req.readyState === 4 && req.status === 200) {
                console.log(req.responseText);
        } else if (req.readyState === 4) {
                throw new Error('XHR Request failed: ' + req.status);
        }
};

req.send(data);

Data is sent using the same format as a query string, but is sent in req.send, not as part of the URL. When sending data, we also set the Content-type header to application/x-www-form-urlencoded.

Writing a Wrapper Function

Instead of writing out a large block of code every time you want to send an AJAX request, we can use a function that will accept arguments such as method, URL, data, and a callback:

function request(method, url, data, callback) {
        if (window.XMLHttpRequest) {
                var req = new XMLHttpRequest();
        } else {
                // Internet Explorer
                var req = new ActiveXObject('Microsoft.XMLHTTP');
        }

        if (method === 'GET' && typeof data === 'string') {
                url += '?' + data;
        }

        req.open(method, url, true);

        if (method === 'POST' && typeof data === 'string') {
                req.setRequestHeader('Content-type',
                        'application/x-www-form-urlencoded');
        }

        req.onreadystatechange = function () {
                if (req.readyState === 4 && req.status === 200) {
                        var contentType = req.getResponseHeader('Content-type');
                        if (contentType === 'application/json') {
                                callback(JSON.parse(req.responseText));
                        } else {
                                callback(req.responseText);
                        }
                } else if (req.readyState === 4) {
                        throw new Error('XHR Request failed: ' + req.status);
                }
        };
        req.send((typeof data === 'string' && method === 'POST') ? data : null);
        return req;
}

You can call it using the following code:

request('GET', '/ajax', 'foo=bar', function (body) {
        console.log(body);
});

You may have noticed that the function includes some code that the original two samples did not; if the Content-type is set to application/json, it attempts to parse it. jQuery does this, too; it’s pretty useful.

You could also create some alias functions, like jQuery does:

function get(url, data, callback) {
        return request('GET', url, data, callback);
}

function post(url, data, callback) {
        return request('POST', url, data, callback);
}

A Simple Application of AJAX

To demonstrate an application of AJAX, we will build a simple page that gets the time from the server. First we will build it without AJAX, and then we will add AJAX support (I’ll explain why I did it in that order in a bit).

Let’s call our page ajax.php, and put the following in it:

<!DOCTYPE html>
<html>
<head>
        <title>time()</title>
        <meta charset="utf-8">
</head>
<body>
        <strong>The time() is:</strong>
        <span id="time"><?php echo time(); ?></span>
</body>
</html>

As we reload the page, it will update. However, what if we want to update it using a button or automatically? We could use a button to refresh the page, or we could use AJAX. First, we will need to check whether the request is an AJAX request, and then if it is AJAX we will return only the value of time(). Save this as ajaxUpdate.php:

<?php

if ($_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
        echo time();
} else {
        echo 'Please go to ajax.php and request this page using AJAX.';
}

?>

This checks whether the request is an AJAX request, and if so it outputs the time. If not, it displays an error to the user. Then we can add the following code to the client side, in order to send the AJAX request:

function updateTime() {
        get('ajaxUpdate.php', '', function (time) {
                document.getElementById('time').innerHTML = time;
        });
}

Whenever updateTime is called, it sends a request to the server, and when it gets a reply it updates the element to display the updated time value. So the final ajax.php is as follows (use it with the original ajaxUpdate.php):

<!DOCTYPE html>
<html>
<head>
        <title>time()</title>
        <meta charset="utf-8">
<body>
        <strong>The time() is:</strong>
        <span id="time"><?php echo time(); ?></span>
        <button>Update time</button>

        <!-- functions.js should contain the get
                and addEventListener functions -->
        <script src="functions.js"></script>
        <script>
                var button = document.getElementsByTagName('button')[0];
                addEventListener(button, 'click', function () {
                        get('ajaxUpdate.php', '', function (time) {
                                document.getElementById('time').innerHTML = time;
                        });
                });
        </script>
</body>
</html>

Designing a Site with AJAX

There are two approaches to writing an AJAX-enabled website. The first is to write the AJAX to begin with, and the second is to design the entire site without AJAX and then add AJAX afterward. There are several advantages and disadvantages of each approach.

Advantages of the first approach:

  • Easier and faster to develop, as it only requires one method to be developed instead of an AJAX and a non-AJAX approach
  • Better support for modern browsers
  • More freedom, as you can use modern features without having to worry about older browsers

Advantages of the second approach:

  • Easier to keep JavaScript and HTML separate, as the HTML doesn’t rely on JavaScript
  • Better support for older browsers and browsers with JavaScript disabled (which is surprisingly common)

Summary

In this shorter chapter, we looked at AJAX: using the XMLHttpRequest object (or ActiveXObject in IE) to send both GET and POST requests, as well as using req.onreadystatechange to get the data that the server sends back. I also explained a couple of ways you can debug sent AJAX requests, and I gave you a wrapper function that you can use to send requests without writing out a huge block of code every time.

Finally, we looked at how we could add AJAX to a simple application, and covered a couple of different approaches to AJAXify applications.

Get Learning from jQuery now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.