Latest Version: 3.7 Final 20 December 2018

Ajax "onProgress" Demo

View the Demo live here

This demo just shows how to use the elementals.js _.ajax method and the 'onprogress' event if supported. It also uses the _.make functions to create the scripting-only controls. I'm of the opinion that if an element only works when JavaScript is running, it shouldn't be in the markup and should be added by the script. To that same end that's why the page has a <noscript> warning on it.

Code explanation

Everything is wrapped in an anonymous function to reduce the odds of variables from this script interfering with the values of other scripts. I pass 'document' to the function as 'd', cute trick that if it's good enough for google, good enough for me. The first thing we do inside said function is create a variable pageWrapper pointing at the outermost wrapper in the HTML, DIV#pageWrapper. We then create an element to wrap our various status messages, progress bars, and controls, along with a container for the result.

(function(d) {
		pageWrapper = _d.getElementById('pageWrapper'),
		statusWrap = _.make('#xmlStatus', { last : pageWrapper }),
		result = _.make('pre#result', { last : pageWrapper }),

We then append a status span into which that actual value will be placed. By nesting make inside make, we can place our text label before this span in a DIV#xmlStateDiv

		status = _.make('span', {
			content : 'Unsent',
			last : _.make('#xmlStateDiv', {
				content : 'XML State: ',
				last : statusWrap

This would be roughly equivalent to doing:

statusWrap.innerHTML += '<div id="xmlStateDiv">XML State: <span></span><div>';

Except that it is done on the DOM avoiding the parser resulting in faster output with less overhead (and therefor less CPU, RAM, and battery consumption), and we have a variable pointing at our span to which states like 'Opened', 'Headers Recieved' or so forth will be placed. This means no ID needed nor a slow document.getElementById every time we want to access it.

Next is a "Start" button the user can clock to, well, start our request.

		start = _.make('a#xmlStart', {
			content : 'Start',
			onclick : function(e) {
				if (progressBar)	_.Class.swap(progressBar, 'waiting','started');
				x.open('get', 'ajaxTest.test', true);
			last : statusWrap

Which is again just using _.make to create a new element, in this case an anchor. We hook a onclick handler to it to call the variable we'll assign our AJAX request to. That handler will clear the 'result' div just to make sure it's empty, and delete the 'start' button so that the user can't just blindly click 'start' in the middle of the request being handled. "progressBar" (which we'll define in a moment) indicates if the AJAX object handles onprogress properly, if it does we swap the class so as to change the colouration in the CSS. Finally it opens our request and sends it. I'm using the normal open/send instead of _.ajax's "request" array since we want to trigger this by a click.

Next up is our actual AJAX handler, x.

		x = _.ajax({
			state : {
				'1' : function(x) { _.Node.write(status, 'Opened', 'rewrite'); },
				'2' : function(x) { _.Node.write(status, 'Headers Recieved', 'rewrite'); },
				'3' : function(x) { _.Node.write(status, 'Loading', 'rewrite'); }
			status : {
				'200' : function(x) {
					_.Node.write(status, 'Completed Ok! Retrieved ' + x.responseText.length + ' Bytes', 'rewrite');
					if (progressBar) {
						_.Class.swap(progressBar, 'started','finishedOk');
						progressBar.style.width = '100%';
					_.Node.write(result, x.responseText, 'rewrite');
				catchAll : function(x) {
					_.Node.write(status, 'Error Code ' + x.status, 'rewrite');
					if (progressBar) {
						_.Class.swap(progressBar, 'started', 'failed');
						progressBar.style.width = '100%';
			progress : function(e) {
				if (e.lengthComputable) {
					var a = Math.ceil(e.loaded * 100 / e.total);
					progressBar.style.width = (a < 100 ? a : 100) + '%';

It starts out with our custom readyState handlers to change the status text as appropriate using _.Node.write using it's "rewrite" parameter. Just flushes out any contents of that element and replaces it with the passed text. Similar to doing status.innerHTML = 'Headers'; except again it does so using the DOM which is faster and lower CPU use by avoiding triggering the browser's parsing engine.

We do not set a readystate 4 handler as we want to use elementals.js' internal handler for that, letting us set our status code handlers instead.

The "200" code handler also does a _.node.write on status to say we did it ok, it then checks to see if there's a progress bar, and if so adjusts the classes and .style.width to reflect that the request has finished. Finally we use _.node.write again to plug the responseText into our result DIV.

The "catchAll" handler will take care of any response code other than 200, reporting the error in the status span, and adjusting the classes and width on the progressBar accordingly.

"progress" is the XMLHTTPRequest.onprogress handler function which will be added IF the browser supports the onprogress event. It tests if the length is known, and if so will adjust the width of our progress bar in percent to the the amount loaded divided by the total, in percent.

Next up we make that progress bar.

			_.make('.waiting', {
				last : _.make('#xmlStatusBar', {
					last : statusWrap
	if (progressBar) progressBar.style.width = 0;

We have to do this after we create or AJAX x variable so we can know if onprogress was assigned and supported. If it's not supported we set it to false, otherwise we make the element, give it the class "waiting" and plug it into a DIV#xmlStatusBar inside our status wrapper.

Finally if progress was created, we set its style.width to 0.

... and that's it. Everything else is handled from the CSS in terms of styling the start button and progress bar.

Not too hard to handle.



Image and Element animated carousel/slideshow.


An unobtrusive lightweight progress bar


On-page smooth scrolling links.