AJAX
Introducing AJAX
In the past, web pages required reloading the whole page every time you wanted to update the content. For example, in web-based email (like gmail.com) this meant that users had to manually reload their inbox to check and see if they had any new email. This had huge drawbacks: it was slow and it required user input (they had to know that they needed to refresh the page to re-check their email). When the user reloaded their inbox, the server had to reconstruct the entire web page and resend all of the HTML, CSS, JavaScript, as well as the data containing the user's email. This was hugely inefficient. Ideally, the server should only have to send the user's new messages, not the entire page. By 2003, all the major browsers solved this issue by adopting JavaScript's XMLHttpRequest
(XHR) object, allowing browsers to communicate with the server without requiring a page reload.
The XMLHttpRequest
object is part of a technology called AJAX (Asynchronous JavaScript and XML). With Ajax, data can now be passed between the browser and the server, using the XMLHttpRequest
API, without having to reload the web page! With the widespread adoption of the XMLHttpRequest
object it quickly became possible to build web applications like Google Maps and Gmail that used XMLHttpRequest
to get new map tiles, or new email without having to reload the entire page.
Working with AJAX
Below is an example of how to create a new XMLHttpRequest object and how to use it to get data and use that data without having to refresh the page. We'll walk through each piece below.
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const jsonData = xhr.responseText;
const data = JSON.parse(jsonData);
const name = data.name;
document.getElementById("demo1").textContent = "JSON results: " + jsonData;
document.getElementById("demo2").textContent = "Parsed JSON's 'name' property: " + data.name;
spaceshipurl = data.starships[0];
console.log(spaceshipurl);
}
};
xhr.open("GET", "http://swapi.co/api/people/1/", true);
xhr.send();
Let's walk through each piece:
Creating the xhr object
const xhr = new XMLHttpRequest();
We oftentimes simplify the name "XMLHttpRequest" to just "XHR". In the first line of our XHR code, we use the built-in JavaScript XMLHttpRequest
constructor to create a new object that we can use to perform an AJAX request.
Next, we're going to skip ahead to the last couple of lines before we talk about the whole middle section with the onreadystatechange
. Understanding the open
and send
methods will give us more insight into the onreadystatechange
event handler.
xhr.open()
xhr.open("GET", "http://swapi.co/api/people/1/", true);
The open
method on the XHR object allows us to specify a few bits of additional information dealing with the AJAX request. In the first parameter, we tell it we want this request to be a "GET" request. If you don't know what that means yet, the short answer is that it's our way of telling the server we want to receive (GET) information rather than submit (POST) information.
The second parameter is the URL address of where this data resides. In the example above, the URL http://swapi.co/api/people/1/
is a location on the web where JSON data about an individual Star Wars character (in this instance, Luke Skywalker who has an ID of 1 in their database) can be found. If you go to that url in a browser, you'll see a page with a bunch of JSON data on it. When you make an XHR request to that URL, the data that comes back is that JSON, which can then be turned into a JavaScript object and used in your web app!
The third parameter is a boolean of whether or not we want the request to be asynchronous. One of the biggest benefits of AJAX is that it is asynchronous, so we'll typically want this to be set to true
.
xhr.send()
xhr.send();
The next line with the send
method is what kicks off the whole process. It simply sends the request to the specified address.
readyState
and onreadystatechange
Every XHR object has a property called readyState
. The readyState
property give us updates on how far along in the process the request is before, during, and after executing the send
method. For example, when you first create the XHR object, it starts at 0, which means "unsent". Once you call the open
method, it changes the readyState
property to 1, which means "opened". Every time the readyState
property changes, the XHR object also fires a readystatechange
event so that we can do things throughout the AJAX process if we want. The XHR object has a method it runs every time there is a readystatechange
event, called onreadystatechange
.
Here's the relevant code from above again:
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const jsonData = xhr.responseText;
const data = JSON.parse(jsonData);
const name = data.name;
document.getElementById("demo1").textContent = "JSON results: " + jsonData;
document.getElementById("demo2").textContent = "Parsed JSON's 'name' property: " + data.name;
spaceshipurl = data.starships[0];
console.log(spaceshipurl);
}
};
In the above code, we set the onreadystatechange
method to our own function, so any time there is a readystatechange
event, it will now run our code.
The first things we're doing in this code is checking what the readyState
and status
properties are. We do this because we need to know for sure that we have successfully received some response data from our request before we can start doing anything with that data. When the readyState
property is 4, that means the server has sent back a response. The response status that comes back from a GET request will be 200
if the request was successful. So in our if statement, we need to check that the readyState
is 4 and the status
is 200.
Once that is true, we can find the data inside the responseText
property of the XHR object (xhr.responseText
).
The data that we got back from the server is in JSON format, which alone doesn't help us access or change anything with plain JavaScript. We need to use JSON.parse()
to change the JSON data into a useable JavaScript object. This is what var data = JSON.parse(jsonData);
is doing. Now we have the data
variable which contains the JavaScript object version of the JSON data we received back from the server.
Using the data
The rest of the code inside the if
block is all about using the data we got back to display the results to the user. While the result on the page may seem trivial (the page goes from being a blank page to having some text on it), it's actually a very revolutionary thing in the history of web development! Users no longer have to submit a form to the server, wait for the server to render a brand new HTML page with all the data the user asked for and wait for the browser to re-load all the same HTML, CSS, and JavaScript that it had already loaded, now with just a few small changes. Instead, the user can now interact with the web page and let all the data exchanges with the server happen in the background while they continue to use the application.
Conclusion
For most of the web applications you write, you probably won't be writing manual XMLHttpRequest objects and checking for readyState changes. Libraries like Axios and jQuery make this process much more developer-friendly. However, understanding what is happening underneath all the magic of libraries that make this easier is helpful and could help you understand problems you may face when using other people's code.