Ajax stands for “Asynchronous JavaScript + XML” (although JSON is mostly used today) and is a group of technologies designed to dynamically create web pages. By using AJAX we improve the quality of interactivity with the user, with the desire to resemble desktop applications as much as possible (according to the speed of interaction). The idea behind Ajax is to load the webpage of the Web application only once, and that any further communication with the server is done asynchronously without blocking the interface and without reloading the entire page.
Asynchronous behavior means that after the user interacts with the interface, the request to the server accepts a JavaScript and XMLHttpRequest object, which in the background sends requests to the server, displaying the results when available, while the user can continue working in the meantime.
Ajax is not without flaws, as Ajax pages dynamically generate the biggest problem for sites is search engine optimization. Search engines are often unable to interpret the site well, causing problems with site indexing. A similar problem exists with Page Traffic Analysis Tools, as a user can spend all day on a single Ajax page, and classic traffic analysis tools will interpret this as a single pageview.
XMLHttpRequest object
XMLHttpRequest is the foundation of AJAX and is a JavaScript object used to send HTTP requests. Designed by Microsoft and then accepted by other major search engines and in 2014 it became standard in W3C.
XHR object properties
readyState
The numerical value that describes the state of an object The possible states are: 0, 1, 2, 3 and 4
state 0: The initial value of the object after creation
state 1: open () method successfully executed; request headsets can be set at this stage with the setRequestHeader () function
state 2: all response heads have arrived
condition 3: starts reading the answer data part
condition 4: The response data part was loaded successfully or an error occurred
responsType
The text data corresponding to the type of server response the possible values are: “”, “arraybuffer”, “blob”, “document”, “json” and “text”
response
A property that accesses the server’s response body
responseText
Returns the server response as a string
responseXML
Returns the server response as XML data
status
A numeric value that corresponds to the HTTP hour of the server response. We are most interested in status 200 – an indication that the request has been successfully processed
statusText
A text message that responds to an HTTP response server message (e.g., “Not Found” or “OK”)
timeout
The numeric value after which the HTTP request will end; the timeout value should be slightly higher than the time provided for response generation; the responseText property value will then be null
withCredentials
A Boolean value that defines whether cross-site Access-Control requires credentials such as cookies, an authorized header, or a TLS certificate.
XHR object methods
abort ()
Cancels the current request
getAllResponseHeaders ()
Returns all header information
getResponseHeader ()
Returns specific header information
open (method, url, async, uname, PSWD)
The function that initiates the HTTP request and the parameters it accepts are:
Method: POST, GET, HEADER, ROAD, DELETE
URL is the URL of the requesting server
Asynchrony can be true or false depending on whether the request realizes asynchronous or synchronous
Username and password are optional arguments that are specified if needed to access the server
send ()
The method by which data is sent to the server (if we have something to send)
setRequestHeader (header, value)
Adds a label/value pair in the header when sending data. It must be called after the open () method and before the send () method
Procedure
a) Creating an object
It is created by invoking the XMLHttpRequest constructor:
var xhr = new XMLHttpRequest();
b) Event handler and a callback function
onreadystatechange
This mode is the oldest and is supported in all browsers. EventHandler monitors every readyState and triggers every state change.
xhr.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200) {
// Process the server response here.
}
};
loadstart
The process started – Once – First
progress
Process in progress – zero or higher – When the loadstart is complete.
error
With this eventListener, we listen to network-level errors:
xhr.addEventListener(error, function(e) {
var error = e.error;
console.log(error);
});
abort
The process is interrupted. – Once or not
load
onreadystatechange “triggers” an event at every change of state of an object, however, we are usually only interested when the state of an object changes to a value: readyState == 4. If you do not care about information about other states and want to reduce the “trigger” of unnecessary events, it may be better newer and more modern approach when only one load event is generated when the server responds:
xhr.addEventListener("load", function(){
if (this.status < 400) {
// Process the server response here.
} else {
new Error("Request failed: " + xhr.statusText);
}, false);
loadend
The entire process is completed – Once – Activated after some error, deliberate abortion or when the load is completed successfully.
c) Defining Connection Parameters
After creating the XMLHttpRequest object, it is necessary to define the basic parameters for communication. This method does not send the request to the webserver but saves its arguments for sending the request later.
xhr.open(method,url,async,username,password)
Description of method arguments:
Method
The methods that can be used are POST, GET, HEADER, PUT, DELETE.
The GET method sends the request and data through the URL, the request can be cached, the request remains in the browser history and can be bookmarked. The disadvantage is the limited amount of data that can be passed through the URL, as well as the visibility of the data in the URL (read “insecurity”). This is why this method is not used to send important data.
The POST method does not send data through a URL but through the send () attribute of the method, so it is more secure and is used when sending confidential information (usually user-entered). The POST method is also used when it is necessary to send a larger amount of data with the consideration that it does not have a limited data size, as is the case with the GET method. However, with this method, the request cannot be cached, it does not remain in browser history, nor can it be bookmarked.
The HEAD method requests the header server from the specified URL without the contents of the document (used, for example, to check the date of change of resources).
URL
URL is the URL of the requesting server
Asynchrony requires
This parameter defines the asynchrony of the request. It can be true or false. If omitted or defined as TRUE then the request is asynchronous.
REMARK:
The XMLHttpRequest request may also be synchronous, but it is not recommended because if the north does not send a response, the send () method may block the browser. There is no “magic button” that stops XMLHttpRequest, no maximum waiting time, so the JavaScript engine does not allow the request to be stopped once sent.
User Name and Password
Username and password are optional arguments that are specified if needed to access the server
d) Defining header information
Syntax
With the setRequestHeader () method, we can define some values in the request header. It is most commonly used when sending data to a server using the POST method. The method parameter is in the form of a key, value pair.
xhr.setRequestHeader(key, value);
REMARK:
The setRequestHeader () method must be located between the open () and send () methods.
Use
Most often, the parameter of the “setRequestHeader ()” method sends data in which way the encoded data is sent.
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
What is encoding?
There are three ways that it can be encoded:
application/x-www-form-urlencoded
By default, all characters are encoded before sending (spaces are converted to “+” symbols, and special characters are converted to ASCII HEX values. Actually, a query string consisting of name/value pairs separated by “&”, where names separated by values are equal to “=” if there are special characters, they are transferred to ASCII HEX values.
multipart/form-data
No characters are encoded. This value is required when using templates with control to upload files
text/plain
Spaces are converted to “+” symbols, but there are no other special characters
The content-type “application / x-www-form-urlencoded” is used in most cases, except for sending large amounts of binary data or text containing non-ASCII characters because it is inefficient for this purpose. And “multipart / form-data” should be used to submit forms containing files, non-ASCII data, and binary data.
e) Preparing data for submission
In the case of sending data, it is necessary to prepare the data in a format that is suitable for sending.
Serializing data in query string
a) Simple information
If the data is simple, it is sent as a query string consisting of name/value pairs separated by the character “&”, where the names are separated from the value by a character equal to “=”, and if there are special characters they are transferred to ASCII HEX values.
var data = "MyVariableOne=ValueOne&MyVariableTwo=ValueTwo";
b) Object
If it is a JSON object, it must first be moved to a string.
var arr = JSON.stringify([ 'foo', 'bar' ]); // Returns: ["foo","bar"]
Then encode the resulting string:
var encoArr = encodeURIComponent(arr) // Returns: %5B%22foo%22%2C%22bar%22%5D
var url = 'http://example.com/?data=' + encoArr ;
You can check decoding and encoding through online services such as https://www.freeformatter.com/url-encoder.html or http://www.url-encode-decode.com/.
Example
This snippet has similar functionality to the jQuery serialize () method
function serialize(form) {
var field, l, s = [];
if (typeof form == 'object' && form.nodeName == "FORM") {
var len = form.elements.length;
for (var i=0; i<len; i++) {
field = form.elements[i];
if (field.name && !field.disabled && field.type != 'file' && field.type != 'reset' && field.type != 'submit' && field.type != 'button') {
if (field.type == 'select-multiple') {
l = form.elements[i].options.length;
for (var j=0; j<l; j++) {
if(field.options[j].selected)
s[s.length] = encodeURIComponent(field.name) + "=" + encodeURIComponent(field.options[j].value);
}
} else if ((field.type != 'checkbox' && field.type != 'radio') || field.checked) {
s[s.length] = encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value);
}
}
}
}
return s.join('&').replace(/%20/g, '+');
}
Serialize data into a series
This snippet has similar functionality to the jQuery serializeArray () method
function serializeArray(form) {
var field, l, s = [];
if (typeof form == 'object' && form.nodeName == "FORM") {
var len = form.elements.length;
for (var i=0; i<len; i++) {
field = form.elements[i];
if (field.name && !field.disabled && field.type != 'file' && field.type != 'reset' && field.type != 'submit' && field.type != 'button') {
if (field.type == 'select-multiple') {
l = form.elements[i].options.length;
for (j=0; j<l; j++) {
if(field.options[j].selected)
s[s.length] = { name: field.name, value: field.options[j].value };
}
} else if ((field.type != 'checkbox' && field.type != 'radio') || field.checked) {
s[s.length] = { name: field.name, value: field.value };
}
}
}
}
return s;
}
f) Submit request
The initiation of connecting a prepared and defined request to the server is done with the send () method. We can use the send method attribute to pass data to the server. Exactly with the POST method, this is the main principle of sending data. We simply send all the “preparation” data as an attribute of the “send” method. However, since the GET method sends all information through a URL, then we do not use this option to send it through an attribute, but instead, define the attribute with a null send (null) or simply not fill it.
g) Server response
The status of the request
The status of our request as a server response is accessed using the XMLHttpRequest property of the status object
xhr.status
and status text with:
xhr.statusText
String answer
The server response in the form of a “string string” is accessed via the responseText property
xhr.responseText
REMARK:
XMLHttpRequest does not have a direct response server property in JSON format, so it is necessary to parse the response:
JSON.parse (xhr.responseText);
XML response
The server response that is some XML is accessed with responseXML
xhr.responseXML
HTTP response server header
Access to all headers returned by the webserver is via the getResponseHeader () method:
xhr.getResponseHeader()
Examples
EXPLANATION:
In the examples, I use the online resource myjson.com to quickly generate REST endpoints.
a) Getting data from the server
Example No.1
I access the JSON file saved at http://myjson.com/ through endpoint: https://api.myjson.com/bins/erxi9. With ajax request towards the endpoint, I get the following content:
[
{
name: "Alen",
position: "CEO"
},
{
name: "John",
position: "Director"
},
{
name: "Mark",
position: "Worker"
},
{
name: "Hasan",
position: "Worker"
},
{
name: "Deen",
position: "Worker"
}
]
This example shows all persons who satisfy the chosen position:
HTML
<label for="position">Select Position</label>
<select id="position">
<option value="ceo">CEO</option>
<option value="director">Director</option>
<option value="worker">Worker</option>
</select>
<br><br><br>
<div id="btn1" class="btn">Get data from server</div>
<div id="output"></div>
SCSS
.btn {
border: 1px solid black;
padding: 8px;
display: inline-block;
background-color:red;
color:white;
margine: 0 auto;
display: block;
text-align:center;
cursor: pointer;
}
#output {
font-size: 24px;
padding: 15px;
text-align: center;
}
Babel
var output = document.getElementById("output");
var btn = document.getElementById("btn1");
var select = document.getElementById("position");
/* the part where the selected option from the dropdown is stored */
var selectPosition= "director";
select.addEventListener("change", function(){
selectPosition= select.options[select.selectedIndex].value;
})
var output = document.getElementById("output");
var btn = document.getElementById("btn1");
var select = document.getElementById("position");
/* the part where the selected option from the dropdown is stored */
var selectPosition= "director";
select.addEventListener("change", function(){
selectPosition= select.options[select.selectedIndex].value;
})
/* Event handler on the button */
btn.addEventListener("click", function() {
/* Cleaning the screen */
output.innerHTML="";
/* New XMLHttpRequest object */
var httpRequest = new XMLHttpRequest();
/* EventHandler for ajax request */
httpRequest.addEventListener("load", function(){
if (this.status < 400) {
/* Data obtained by ajax */
var myObj = JSON.parse(this.responseText);
/* Print only selected positions */
var SelectArray= myObj.filter(function (obj) {
return obj.position== selectPosition;
});
for (let select in SelectArray) {
output.innerHTML += SelectArray[select].position + " " + SelectArray[select].name+ "<br>";
}
} else {
var errors= new Error("Request failed: " + httpRequest.statusText);
console.log("Server is OK but some transport error");
}
}, false);
/* Opening a connection and defining parameters */
httpRequest.open("GET", "https://api.myjson.com/bins/erxi9", true);
httpRequest.send();
}, false )
Example No.2
For this example, I use the generated endpoint https://api.myjson.com/bins/amz5t.
<p>(Titles obtained by AJAX)</p>
<h1 id="title"></h1>
<h3 id="description"></h3>
<hr>
<div>
<p>(Links to articles were provided by AJAX)</p>
<div id="text"></div>
</div>
TypeScript
function ajax_get(url, callback) {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
try {
var data = JSON.parse(xmlhttp.responseText);
} catch(err) {
console.log(err.message + " in " + xmlhttp.responseText);
return;
}
callback(data);
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
ajax_get('https://api.myjson.com/bins/amz5t', function(data){
var eNaslov = document.getElementById("title");
var eOpis = document.getElementById("description");
var eArtikli = document.getElementById("text");
var serverArtikli = '';
serverArtikli += "<ul>";
for (var i=0; i < data["articles"].length; i++) {
serverArtikli += '<li><a href="' + data["articles"][i]["url"] + '">' + data["articles"][i]["title"] + "</a></li>";
}
serverArtikli += "</ul>";
eNaslov.innerHTML = data["title"]; //
eOpis.innerHTML = data["description"];
eArtikli.innerHTML = serverArtikli;
})
b) Uploading data to the server
Example
var newName = 'John Smith';
var xhr = new XMLHttpRequest();
xhr.open('POST', 'myservice/username?id=some-unique-id');
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
if (xhr.status === 200 && xhr.responseText !== newName) {
alert('Something went wrong. Name is now ' + xhr.responseText);
}
else if (xhr.status !== 200) {
alert('Request failed. Returned status of ' + xhr.status);
}
};
xhr.send(encodeURI('name=' + newName));
Example: submitting form information
document.getElementById("myform").onsubmit = function(e) {
e.preventDefault();
var f = e.target,
formData = '',
xhr = new XMLHttpRequest();
// fetch form values
for (var i = 0, d, v; i < f.elements.length; i++) {
d = f.elements[i];
if (d.name && d.value) {
v = (d.type == "checkbox" || d.type == "radio" ? (d.checked ? d.value : '') : d.value);
if (v) formData += d.name + "=" + escape(v) + "&";
}
}
xhr.open("POST", f.action);
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
xhr.send(formData);
}
c) Uploading files to the server
var file = document.getElementById('test-input').files[0];
var xhr = new XMLHttpRequest();
xhr.open('POST', 'myserver/uploads');
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);