AJAX - XMLHttpRequest

For this activity, I'm assuming that you have Apache and PHP installed on your computer.

Download this .zip file, extract it, and then put the extracted folder (ajax-xml-http) in your doc root directory. You'll add your JavaScript code to the .html file. The PHP file will be used throughout the activity, but you will not need to make any changes to it (we may tinker with it in class).

Getting Started with AJAX

AJAX is s very important concept for web developers to understand, because it controls the communication between the client (your app running in a user's browser) and the server. Programmers will often talk about 'AJAX calls'. An AJAX call is a way of using JavaScript code to make HTTP requests from a web program.

You can use AJAX to fetch information from a server without having to redirect to another page. This is an important concept to understand because modern web applications often consist of a single web page. This is very different from a traditional website made up of many different .html files that are linked together. In a modern single page web app (SPA), if you need to display new data to the user, you do it on the same page rather than redirecting the user to different page. This can make your web apps preform faster because they are not constantly downloading .html files.

Instead of redirecting to a new page that must be downloaded in full, you can use AJAX to fetch raw data (rather than load an entire .html file).

If you don't know about AJAX, this is a monumental moment in your web development career. Modern web applications are built to communicate with servers by making AJAX calls. An AJAX call is code that you write which instructs your program to fetch information from a server on the web. Modern web apps are constantly making AJAX calls to fetch information, often from many various servers. To see this in action, log into your GMAIL account and observe the Network tab of the Web Developer Tools (press F12 to open the web dev tools and then click on the Network tab). Here, if you are patient, you'll be able to see all sorts of AJAX calls happening in the background.

AJAX is made more complicated for beginning programmers because the use of it has evolved since it was first introduced back in the early 2000s.

I'm going to show you how it was originally used first, then we'll look at modern ways of making AJAX calls.

Here's an example of the old school way of doing AJAX calls. First you need to instantiate an XMLHttpRequest object (add this code to the SCRIPT element of the .html page):

const http = new XMLHttpRequest();
console.log(http);

Make sure to run this code and inspect the object in the console log. There a lots of properties and methods in an XMLHttpRequest object.

The constructor call returns an XMLHttpRequest object which we are storing in a variable named http. As you know, we could have used any variable name, but I chose 'http' because this object has methods that allow us to send HTTP requests from a web page.

Now that we have instantiated the XMLHttpRequest object, there are two methods that we can call on it to send and AJAX request. We can use the open() method to set up an HTTP request.

Add this code to the SCRIPT element in the .html file:

const url = "ajax-request-handler.php";
http.open("GET", url);

Note that the open() method takes two paramaters. The first is a string that sets the method of the request, in this case we'll send a GET request. The second parameter is the URL to send the request to. Normally this would be a URL that starts with https:// but in this activity we are sending the requests to the ajax-request-handler.php script, which was included in the zip file for this activity.

Now we'll start listening for some of the events that are emitted by an XMLHttpRequest object. Add this code to the SCRIPT element of the .html page:

http.addEventListener("readystatechange", function(){
    if(http.readyState == 4 && http.status == 200) {
        successCallback(http.responseText);
    }else if(http.readyState == 4){
    	errorCallback(http.status, http.statusText);
    }
});

An XMLHttpRequest object will emit a "readystatechange" event as the request is in progress. Sending an HTTP request, and handling the response is actually a very complicated process (which you may learn more about in your Cisco class). There are lots of steps involved in connecting to a server and getting a response from it. You can track the progress of the AJAX call by hooking into the 'readystatechange' event. Each time a 'readystatechange' event occurs, you can check the readyState and status properties of the object to find out if the request was a success (meaning that the server received the request and was able to respond to it). A 'readyState' of 4 indicates that the request is finished, although it could have resulted in an error. If the status of the response is 200, it indicates that the request was a success, and that we have received the data we requested from the server. To get the response from the server, we access responseText property of an XMLHttpRequest object.

Note that there are functions being called within each branch of the IF statement. We need to define them before we run the code.

Add these two methods:

const successCallback = (response) => {
	alert("SUCCESS (here's the response):\n" + response);
}

const errorCallback = (status, msg) => {
	alert("ERROR\n" + status + "\n" + msg);
}

Our successCallback() function will be called/triggered when the AJAX call succeeds, and the response from the server will be passed in as a parameter.

The errorCallback() function will be called/triggered if the AJAX call results in failure (which means that the server responded with a status other than 200).

FOLLOW UP QUESTION TO THINK ABOUT: What are some reasons that an AJAX call (an HTTP request) may not work?

Now that we have created an XMLHttpRequest object and configured it by specifying the method, url, and the readystatechange event handling code, we can go ahead and send the request by calling the send() method (add this code):

http.send();

Now make sure you load the ajax.html page via the localhost address (AJAX calls usually don't work when you load the .html page via the file system)

Hopefully you got a response of "Hello!" from the server.

This has been a very quick run through of an XMLHttpRequest object in JavaScript.

There is an ineresting history behind the XMLHttpRequest object. It was originally designed by Microsoft for use with mail servers. But web developers discovered how you could make requests without leaving a page and it revolutionized the web.

An interesting experiment is to put an alert() just after calling the send() method. Please remind me to do this in class so that we can have a discussion about asynchronous programming.

Comment out all the JavaScript code we've added to the page.

Now we'll create a function that makes the code we've just studied reusable. Add this function to the page:

function doGetRequest(url, successCallback, errorCallback){

	const http = new XMLHttpRequest();
	
	http.open("GET", url);
    
    http.addEventListener("readystatechange", () => {
        if(http.readyState == 4 && http.status == 200) {
            successCallback(http.responseText);
        }else if(http.readyState == 4){
        	errorCallback(http.status, http.statusText);
        }
    });

    http.send();
}

Now, instead of repeating the code that creates, configures, and sends the HTTP request, we can simply call this function whenever we want to make a GET request.

The code in the body of the function is identical to what we have previously done. But now we've 'wrapped' it in a function that allows us to send various AJAX calls without repeating the code for each one. We have the flexibility to change the URL, and the success and error behavior by passing in parameters.

The doGetRequest() function defines 3 parameters:

Go ahead and try out this function by invoking it and passing in the required parameters.

doGetRequest(
	"ajax-request-handler.php", 
	function(response){
		alert(response);
	},
	function(status, msg){
		alert(status + msg);
	}
)

The last two parameters, which are functions, are known as callback functions. They are functions passed as parameters that may get called at a later time. You have already used callbacks when you explored event handling. Whenever you call the addEventListener() method, the second parameter is a callback function, which may, or may not get called later (it depends on whether or not the event that you are listening for actually occurs). Callback functions are also used heavily in JavaScript as a way to deal with code that may fail. This is why we specify an error callback as well as a success callback. Finally, callback functions are used heavily in asynchronous programming for operations that may not only fail, but may also take a long time to succeed. An HTTP request may be delayed by any number of factors (slow bandwidth, slow DNS resolution, etc.)

Note that we used anonymous functions when we called doGetRequest() above. In the next example we'll use named functions for the callbacks. Either way is acceptable, but the nice thing about naming a function is that you can reuse it (just call it by it's name).

Finally, just for fun, let's set up the button on the page to send an AJAX call when it's clicked.

window.addEventListener("load", () => {

	const successHandler = (response) => {
		alert("SUCCESS (here's the response):\n" + response);
	}

	const errorHandler = (status, msg) => {
		alert("ERROR\n" + status + msg);
	}

	document.querySelector("[name=btnDoGetRequest]").addEventListener("click", ()=>{
		doGetRequest("ajax-request-handler.php", successHandler, errorHandler);
	});

})

You can learn about all the ins and outs of the XMLHttpRequest API here.

FOLLOW UP QUESTIONS (from the above code sample):

FOLLOW UP ACTIVITY

In class we may fiddle with the PHP file, which will alter the response for our AJAX calls.