PROWARE technologies
PROWARE technologies

AJAX Library

Note: if looking for an AJAX library to use with React then see AJAX Library for React.

This is what AJAX looks like without a library to simplify it.

<!DOCTYPE html>
<!-- basic_ajax.html -->
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>Basic AJAX Example</title>
	<style>
	pre {
		white-space: normal;
	}
	</style>
	<script>
	function getFile(file) {
		var xmlobj;
		if (typeof XMLHttpRequest != "undefined") {
			xmlobj = new XMLHttpRequest();
		}
		else if (window.ActiveXObject) {
			var aVersions = ["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", "Microsoft.XMLHttp"];
			for (var i = 0; i < aVersions.length; i++) {
				try {
					xmlobj = new ActiveXObject(aVersions[i]);
					break;
				} catch (err) {
				}
			}
		}
		if(xmlobj) {
			xmlobj.onreadystatechange = function () {
				if (xmlobj.readyState == 4 && xmlobj.status == 200) {
					document.getElementById("html").innerText = xmlobj.responseText;
				}
			}
			xmlobj.open("get", file, true);
			xmlobj.send(null);
		}
		return false;
	}
	</script>
</head>
<body>
	<pre id="html"></pre>
	<a onclick="return getFile('Lorem.txt');" href="#">Get the Lorem.txt file</a>
</body>
</html>

Here is the above file using the AJAX library (ajaxlib.js) that follows.

<!DOCTYPE html>
<!-- ajax_lib.html -->
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>AJAX Library Example</title>
	<style>
	pre {
		white-space: normal;
	}
	</style>
	<script src="ajaxlib.js"></script>
</head>
<body>
	<pre id="txt"></pre>
	<a onclick="return ajax({
		path: 'Lorem.txt',
		onsuccess: function(responseText) {
			document.getElementById('txt').innerText = responseText;
		}
	});" href="#">Get the Lorem.txt file using ajaxlib.js</a>
</body>
</html>

ajaxlib.js

These following are functions commonly used in AJAX. The ajax function submits the AJAX request. The buildFormData function returns a FormData object or a string built from a form object to use in the body of the post (data: of the ajax() function request object). It can also calculate the size of the data in the form when specifying true for the returnSizeOnly parameter. Make sure to set the form's enctype attribute otherwise the buildFormData function won't be able to work with "multipart/form-data" forms.

The ajax function calls a method function of the request object — if specified — with the percentage completion of the upload so that a progress indicator can be displayed for the user.

This code has been tested on IE6 and newer browsers like Chrome, Safari and Firefox.

// ajaxlib.js
// add additional, custom fields here if so desired;
// returnSizeOnly=true will calculate the size of the form's data
function buildFormData(form, returnSizeOnly) {
	var size = 0;
	var params = new Array();
	var fd;
	if (!returnSizeOnly && form.enctype.toLowerCase() == "multipart/form-data" && typeof FormData != "undefined") {
		fd = new FormData();
	}
	var len = form.elements.length;
	for (var i = 0; i < len; i++) {
		var field = form.elements[i];
		if (field.name && field.name.length > 0) {
			switch (field.type) {
				case "select-one":
				case "select-multiple":
					if (field.name.length) {
						var j, optLen;
						for (j = 0, optLen = field.options.length; j < optLen; j++) {
							option = field.options[j];
							if (option.selected) {
								var optValue = "";
								if (option.hasAttribute) {
									optValue = (option.hasAttribute("value") ? option.value : option.text);
								} else {
									optValue = (option.attributes["value"].specified ? option.value : option.text);
								}
								if (fd) {
									fd.append(field.name, optValue);
								}
								else {
									params.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(optValue));
								}
								size += optValue.length;
							}
						}
					}
					break;
				case "submit":
				case "reset":
				case "button":
				case undefined:
					break;
				case "file":
					for (var j = 0; j < field.files.length; j++) {
						if (fd) {
							fd.append(field.name, field.files[j], field.files[j].name)
						}
						size += field.files[j].size;
					}
					break;
				case "checkbox":
				case "radio":
					if (!field.checked) {
						break;
					}
				default:
					if (fd) {
						fd.append(field.name, field.value)
					}
					else {
						params.push(encodeURIComponent(field.name) + "=" + encodeURIComponent(field.value));
					}
					size += field.value.length;
			}
		}
	}
	return (returnSizeOnly ? size : (fd ? fd : params.join("&")));
}
/*
request = {
	verb: "GET POST PUT DELETE etc.",
	path: "",
	contentType: "application/json; charset=utf-8, application/x-www-form-urlencoded; charset=utf-8, etc.",
	headers: {"header1":"value1","header2":"value2"},
	data: "string" or FormData,
	onprogress: function,
	onsuccess: function,
	onfailure: function
};
*/
function ajax(request) {
	var obj = "object";
	if (typeof request != obj) { request = {}; }
	var func = "function";
	var undef = "undefined";
	var xmlobj;
	if (typeof XMLHttpRequest != undef) { // must use typeof operator for compatibility reasons
		xmlobj = new XMLHttpRequest();
	}
	else if (window.ActiveXObject) {
		var aVersions = ["MSXML2.XMLHttp.5.0", "MSXML2.XMLHttp.4.0", "MSXML2.XMLHttp.3.0", "MSXML2.XMLHttp", "Microsoft.XMLHttp"];
		for (var i = 0; i < aVersions.length; i++) {
			try {
				xmlobj = new ActiveXObject(aVersions[i]);
				break;
			} catch (err) {
				//void
			}
		}
	}
	var hasFailure = (typeof request.onfailure == func);
	if (typeof xmlobj != obj) {
		if (hasFailure) {
			request.onfailure(0, ""); // send a status of zero for this error
		}
		return false;
	}
	var hasProgess = (typeof request.onprogress == func && typeof xmlobj.upload == obj);
	if (hasProgess) {
		xmlobj.upload.addEventListener("progress", function (event) {
			request.onprogress(Math.floor(event.loaded / event.total * 100));
		});
	}
	xmlobj.onreadystatechange = function () {
		if (xmlobj.readyState == 4) {
			if (hasProgess) {
				request.onprogress(null);
			}
			if (xmlobj.status == 200) {
				if (typeof request.onsuccess == func) {
					request.onsuccess(xmlobj.responseText);
				}
			}
			else if (hasFailure) {
				request.onfailure(xmlobj.status, xmlobj.statusText);
			}
		}
	};
	// if no verb is specified then use "get"; if no path is specified then use the current file
	xmlobj.open(request.verb || "get", request.path || location.pathname, true);
	if (request.contentType) {
		if (typeof FormData == undef || !(request.data instanceof FormData)) {
			xmlobj.setRequestHeader("Content-Type", request.contentType);
		}
	}
	if(typeof request.headers == obj) {
		for(var prop in request.headers) {
			xmlobj.setRequestHeader(prop, request.headers[prop]);
		}
	}
	xmlobj.send(request.data || null);
	return false; // to prevent forms and links from submitting
}

Here is example code using the above AJAX library.

// get the current page
var request = {
	onsuccess: function (responseText) { alert(responseText); }
};
ajax(request);
// get /api/customers data
var request = {
	path: "/api/customers",
	onsuccess: function (responseText) { var customers = JSON.parse(responseText); }
};
ajax(request);
// post a form
var form = document.getElementById("form-id");
function progress(percent) {
	var progressBar = document.getElementById("progress-bar");
	progressBar.style.backgroundColor = "red";
	progressBar.style.height = "16px";
	if(percent) {
		progressBar.innerText = progressBar.style.width = percent + "%";
	}
	else {
		progressBar.innerText = progressBar.style.width = "";
	}
}
function success(responseText) {
	alert(responseText);
}
var request = {
	verb: form.method,
	path: form.action,
	contentType: "application/x-www-form-urlencoded; charset=utf-8",
	data: buildFormData(form),
	headers: {"Authorization":"auth token","referer":"http://localhost/index.html"},
	onprogress: progress,
	onsuccess: success
};
ajax(request);
// post a multipart/form-data form
var form = document.getElementById("form-id");
function progress(percent) {
	var progressBar = document.getElementById("progress-bar");
	progressBar.style.backgroundColor = "green";
	progressBar.style.height = "20px";
	if(percent) {
		progressBar.innerText = progressBar.style.width = percent + "%";
	}
	else {
		progressBar.innerText = progressBar.style.width = "";
	}
}
function success(responseText) {
	alert(responseText);
}
function failure(status, text) {
	alert(status + ": " + text);
}
var request = {
	verb: form.method,
	path: form.action,
	data: buildFormData(form),
	onprogress: progress,
	onsuccess: success,
	onfailure: failure
};
ajax(request);
// post json and get json returned
ajax({
	verb: "post",
	path: "/api/addcustomer",
	contentType: "application/json",
	data: JSON.stringify({ firstname: form.firstname, lastname: form.lastname }),
	onprogress: function(percent) { /* do something */ },
	onsuccess: function(responseText) { var obj = JSON.parse(responseText); },
	onfailure: function(status, text) { alert(status + ": " + text); }
});
<!DOCTYPE html>
<html>
<head>
	<title></title>
	<script src="ajaxlib.js"></script> <!--download from above-->
</head>
<body>
	<pre id="html"></pre>
	<a onclick="return ajax({ onsuccess: function (res) { document.getElementById('html').innerText = res; } });" href="#">get the current page</a>
	<form enctype="multipart/form-data" action="/" method="post" onsubmit="return ajax({
	verb: this.method,
	path: this.action,
	contentType: this.enctype,
	data: buildFormData(this),
	onprogress: function (percent) {
			if (percent)
				document.getElementById('progress').style.width = percent + '%';
			else
				document.getElementById('progress').style.width = 0;
		},
	onsuccess: function (res) {
			alert(res);
		},
	onfailure: function(status, text) {
			alert(status + ' ' + text);
		}
	});">
		<input type="text" name="name" />
		<input type="file" name="file" multiple />
		<input type="submit" value="post to home page" />
	</form>
	<div id="progress" style="width:0;height:16px;background-color:red;"></div>
</body>
</html>

Here is a simple Node.js server that serves static files and echos data that is posted to it. It is useful for testing AJAX code. Learn more about Node.js.

var fs = require("fs");
const http = require("http");
const server = http.createServer().listen(8000);
console.log("listening on port 8000");

server.on("request", function (request, response) {
	if (request.method == "POST") {
		var body = "";
		request.on("data", function (data) {
			body += data.toString();
		});
		request.on("end", function () {
			console.log(body);
			var bodyArray = body.split('&');
			var obj = {};
			for(var i = 0; i < bodyArray.length; i++) {
				var temp = bodyArray[i].split('=');
				if(temp.length == 2)
					obj[temp[0]] = decodeURIComponent(temp[1].replace(/\+/g, "%20"));
				else
					obj[temp[0]] = null;
			}
			// ECHO THE DATA BACK TO THE CLIENT
			response.writeHead(200, {"Content-Type":"application/json"});
			response.write(JSON.stringify(obj));
			response.end();
		});
	}
	else if (request.method == "GET") { // SERVE STATIC HTML FILES
		console.log(request.url);
		var file = request.url.substring(1, request.url.length);
		if(file.length == 0)
			file = "index.html";
		fs.readFile(file, function (err, data) {
			if (!err) {
				response.writeHead(200, {"Content-Type":"text/html"});
				response.end(data.toString('utf8'));
			}
			else {
				response.writeHead(404, {"Content-Type":"text/plain"});
				response.end(`path ${request.url} not found`);				
			}
		});
	}
	else {
		response.writeHead(405, {"Content-Type":"text/plain"});
		response.end(`method ${request.method} not allowed`);
	}
});

This file was used with the above node.js server.

<!DOCTYPE html>
<!-- ajax_lib_form.html -->
<html lang="en">
<head>
	<title>AJAX Library Example Form</title>
	<script src="ajaxlib.js"></script>
	<script>
	function submitForm(form) {
		return ajax({
			verb: form.method,
			path: form.action,
			contentType: form.enctype,
			data: buildFormData(form),
			onsuccess: function(responseText) {
				document.getElementById('txt').innerText = responseText; // this server will return JSON 
			},
			onprogress: function(percentage) {
				if (percentage) {
					document.getElementById('percent').innerText = percentage + '%';
				}
			},
			onfailure: function(errorCode, errorText) {
				alert(errorCode + ': ' + errorText);
			}
		});
	}
	</script>
</head>
<body>
	<pre id="txt"></pre>
	<form action="/" method="POST" enctype="application/x-www-form-urlencoded" onsubmit="return submitForm(this);">
	<input type="text" name="name" placeholder="enter name" /><br />
	<input type="text" name="address" placeholder="enter address" /><br />
	<input type="text" name="state" placeholder="enter state" /><br />
	<input type="text" name="zip" placeholder="enter zip" /><br />
	<input type="submit" />
	</form>
	<div id="percent"></div>
</body>
</html>

Coding Video

https://youtu.be/KnL5itkAy9A