AJAX Library (ajaxlib.js)

These 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 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 function — 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("&")));
}
function ajax(request) {
	if (typeof request != "object") { 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 (!xmlobj) {
		if (hasFailure) {
			request.onfailure(0, ""); // send a status of zero for this error
		}
		return false;
	}
	var hasProgess = (typeof request.onprogress == func && xmlobj.upload);
	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(false);
			}
			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);
		}
	}
	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),
	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); }
});
<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>

Coding Video

https://youtu.be/vgAxaJu8hZA