This is my compilation of a full explanation of Ajax from the bottom up. I wanted to put everything about Ajax in one easily-accessible place for anyone who remains confused by this confusing aspect of JS.
I will use the PHP language throughout this article as my server-side language
By the time you finish reading this post, you should be able to understand every aspect of e.g. this code:
function doSomething(jsonToJS) {
// Do something on the current page..
}
function getAjax() {
let id = document.getElementById("form-control");
let req = new XMLHttpRequest();
let fd = new FormData(id);
req.open('post', '/path/to/ajaxification.php');
req.send(fd);
req.upload.addEventListener('progress', function() {
// Do something to show the progress of the upload..
});
req.upload.addEventListener('error', function() {
// Print something to show the error in the upload..
});
// Downloading the response from the server:
req.addEventListener('load', function() {
let rawResponse = req.responseText;
// console.log(rawResponse);
let jsonToJS = JSON.parse(rawResponse);
doSomething(jsonToJS);
// OR:
window.location.assign('/some-location?' + 'key1=val1');
});
}
Disclaimer: This is a simplified version of asynchrony etc. Any mistakes are my own alone. This page also presents only one way of doing Ajax: modern enough not to be quite as convoluted as the Ajax of old, yet not so modern that most Ajax you see in the wild will be radically different than what I present here.
AJAX stands for Asynchronous JavaScript And XML. Today it's mostly used with JSON rather than XML, but Ajax sounds better than AJAJ.
Here's an explanation of Ajax in terms of its component parts: what 'asynchronous' really means, what 'XMLHTTPRequest
' and its basic Ajax functions are, and how to use Ajax at all stages of the HTTP request / response cycle.
JS is a 'non-blocking, single-threaded, asynchronous language'. An asynchronous language has a set of built-in asynchronous functions, which we define below. But that's it: nothing more fancy about a language being 'asynchronous' than 'having built-in asynchronous functions'.
Traditional languages are not asynchronous, i.e., they have no built-in asynchronous functions.
Let's break down exactly what's meant by 'non-blocking', 'single-threaded', and 'asynchronous':
EventEmitter
' object within them, but this is out of the scope of this article.Here is the a very simple way to show how synchronous functions work.
'console.log(…)
' is a synchronous function.
'setTimeout(…)
' is an asynchronous function:
console.log(1);
setTimeout(function(){
console.log(2);}, 5000);
console.log(3);
// OUTPUT:
1
3
[hangs for 5 seconds, then:] 2
If 'setTimeout()'
were synchronous, we'd have very different output:
console.log(1);
// If 'setTimeout' were synchronous:
setTimeout(function(){
console.log(2);}, 5000);
console.log(3);
// OUTPUT:
1
[hangs for 5 seconds, then:] 2
3
XMLHttpRequest
ObjectJS uses the XMLHTTPRequest
object (the official Ajax object) to send data from the client-side to the server-side, and optionally to receive data from the server-side back to the client-side when the data it sent is finished processing.
Here are the standard functions of the XMLHttpRequest
object:
function someFunction() {
// Do something on this page
}
function doAjax() {
let request = new XMLHttpRequest();
request.open('get', '/path/to/ajaxification.php');
request.send();
request.addEventListener('load', function(event) {
let rawResponse = req.responseText;
let jsonToJS = JSON.parse(rawResponse);
someFunction(jsonToJS);
});
}
XMLHttpRequest
Object Meanrequest.open(…)
'. This creates (initializes) a new request with the HTTP verbs GET, POST, PUT, etc., as well as telling Ajax where to send this request off to. I'll cover the two most common request types here (also the only two that browsers actually understand, though we've developed ways of getting around this).
request.open('get', 'path/to/ajaxification.php?' + 'key1=val1&' + someJSVariable);
'. Because GET requests extract all of their information from the URL, it is possible to place a query string in this URL when initializing a GET request in Ajax:request.open("post", "path/to/ajaxification.php")
'. With the POST method, you have the option of changing the type of content you send up to the server with 'request.setRequestHeader(…)
'.request.send(…)
'. Now that you've initialized your request, request.send(…)
actually sends the request to the server.
request.send()
' with GET. GET methods contain no HTTP body, so you just write request.send()
or request.send(null)
request.send(body)
' with POST. The body
sent to the server with a POST request can be many things, all of which require you to tell the server what it's receiving in a header.
<form>
.FormData
object, which includes an implicit setRequestHeader(…)
so you don't have to worry about it:
let id = document.getElementById("form-control");
let req = new XMLHttpRequest();
let fd = new FormData(id);
req.open('post', '/path/to/ajaxification.php');
req.send(fd);
/path/to/ajaxification.php
': The Backend Page That You Send Your Request Off ToEvery Ajax request should be processed on the server.
The server should have it's own page for all Ajax requests, separate from the webpage where Ajax is called: in this case, we've called it 'ajaxification.php
'
Note that in this case its a PHP page, but the backend language you're using could be anything: Node, Java, etc.
You can see the results of ajaxification.php
when the server returns its response.
ajaxification.php
's response is returned to the Ajax request in its raw form as 'req.responseText
'
The response from ajaxification.php
should be in JSON.
If you have errors in the JSON you've output from ajaxification.php
however, then req.responseText
has the entire page within it, which is especially good for debugging.
Once you've debugged, you should JSON.parse(req.responseText)
the JSON you got in the response. JSON.parse(req.responseText)
converts the req.responseText
from JSON into pure JS.
request.setRequestHeader(header, value)
'The 'req.setRequestHeader(header, value)
' method allows you to notify the server of the type of request you're sending to it, which is necessary for an error-free response:
// Default (usable with basic POST requests):
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
// POSTing media, <form>s:
req.setRequestHeader('Content-Type', 'multipart/form-data');
There are a few things to be aware of when using the req.setRequestHeader(header, value)
method:
setRequestHeader(…)
adds an additional HTTP request header to the list.setRequestHeader(…)
must be called after calling req.open(…)
, andsetRequestHeader(…)
must be called before calling req.send(…)
. For example:
let id = document.getElementById("form-control");
let req = new XMLHttpRequest();
let fd = new FormData(id);
req.open('post', 'path/to/ajaxification.php');
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setRequestHeader('X-CSRF-TOKEN', 'dsa45hfd436hsstr46hwer56663sgf');
req.send(fd);
upload
', sandwiched between XMLHttpRequest's 'req.
' object and the '.addEventLisener
method, following the normal rules of chaining:
// UPLOADS:
let req = new XMLHttpRequest();
req.upload.addEventListener("loadstart", callback);
req.upload.addEventListener("progress", callback);
req.upload.addEventListener("loadend", callback);
req.upload.addEventListener("load", callback);
req.upload.addEventListener("error", callback);
req.upload.addEventListener("abort", callback);
req.open(…);
req.send(…);
XMLHttpRequest
object handles downloads without having to specify any additional sandwiched objects in between req
and addEventListener
:
// DOWNLOADS:
let req = new XMLHttpRequest();
req.addEventListener("loadstart", callback);
req.addEventListener("progress", callback);
req.addEventListener("loadend", callback);
req.addEventListener("load", callback);
req.addEventListener("error", callback);
req.addEventListener("abort", callback);
req.open(…);
req.send(…);
.addEventListener(eventTypes, …)
' MeanBoth uploads and downloads through Ajax have the same event types ('.addEventListener(eventTypes, …)
') for showing Ajax's progress in uploading to the server, and downloading the server's response. Here's what they mean:
loadstart
, progress
, loadend
. These show the point at which your upload / download started, the progress Ajax is making in uploading / downloading the file (periodic responses from the XMLHttpRequest
object), and the final response when the file / JSON / etc. has finished uploading / downloading with loadend
load
. Any messages you want to give when the file has finished uploading or downloading.
load
-ing whatever the server sends, this comes with 'req.responseText
' (see ajaxification.php
and debugging).error
. Non-HTTP errors that may occur with the upload / download. This is generally used to print something.abort
. Usually used as part of an 'if
' statement, if at all, this event type allows you to abort the Ajax request if e.g., the user tries to upload a certain kind of file etc.
<!-- In any script included here, all global-scoped JS variables are accessible: -->
<script src="includes/themes/mgm-default-theme/js/highlight.js"></script>
<script src="includes/themes/mgm-default-theme/js/homepage.js"></script>
<?php
$php_var = array('yes' => 'hi', 'no' => 'bye');
$php_var = json_encode($php_var);
?>
<script>
// For first printing the page:
let jsVar = <?php echo $php_var; ?>
console.log(jsVar);
// OUTPUT on console: Object { yes: "hi", no: "bye" }
</script>
ajaxification.php
.
let req = new XMLHttpRequest();
req.open('get', 'path/to/ajaxification.php' + '?first=' + jsVar);
req.send();
let id = document.getElementById("form-control");
let req = new XMLHttpRequest();
let fd = new FormData(id);
req.open('post', 'path/to/ajaxification.php');
req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
req.setRequestHeader('X-CSRF-TOKEN', 'dsa45hfd436hsstr46hwer56663sgf');
req.send(fd);
ajaxification.php
. For this, you have to:
echo
out whatever you want to send back:
$json_to_PHP = json_decode($_POST['jsVar']);
// Manipulate $json_to_PHP here..
echo json_encode($any_PHP_var_you_like_including_json_to_PHP);
req.addEventListener('load', function(event) {
let jsObj = JSON.parse(req.responseText);
// jsObj is a JS Object which contains anything sent by the server:
// jsObj.serverVar1, jsObj.serverVar2, etc.
doSomething(jsObj);
});
load
' event type to cause a redirect, with no server involvement whatsoever (easing your HTTP requests):
req.addEventListener('load', function(event) {
window.location.assign('/some-location?' + 'key1=val1');
});
FormData
object or JSON.stringify(…)
(a way to convert JS data into a JSON string) for anything you send up to the server with req.send(body)
JSON.stringify(withVarables)
. Put in literals only.ajaxification.php
(I won't go into how to do this here, but take a look at OWASP's site to get an idea of how to do this).json_encode(…)
only to encode associative arrays.
req.responseText(…)
.
console.log(req.responseText);
has not yet been converted from JSON to JS: it is literally the raw response of anything on the ajaxification.php
page, namely any PHP errors within ajaxification.php
, or anything var_dump(…)
ed in PHP, will be console.log()
ed out to the console.error_log($JS_variables)
' In ajaxification.php
.