AJAX progress bar

With few lines of JavaScript and CSS it’s possible to make simple AJAX progress bar. JavaScript will periodically ask for progress value and server will respond with JSON data. Progress value is being read and displayed as DIV element width. To start progress bar demo, click on Start button and wait few seconds to begin. To watch how this page sends requests to Web server just open DOM inspector “Network” card.

 


More precisely, every 1000ms sendRequest() method sends request to server – see setInterval line in pollingStart() method. ajax-progress-bar.php responds and requestHandler() processes received JSON data. Progress value in JSON data has meaning of job completion (in percentage).

In following JavaScript source, please focus on sending and handling requests. Property onreadystatechange is function that receives server feedback. It’s important to note that feedback function must be assigned before each send, because after request is completed, onreadystatechange property is cleared.

// create redips container
let redips = {};

// initialization
redips.init = function () {
    // set reference to the progress bar DIV
    redips.div = document.getElementById('progress');
    // create XML HTTP request object
    redips.xhr = new XMLHttpRequest();
    // set interval ID to false
    redips.intervalID = false;
};

// send request to server
redips.sendRequest = function () {
    let numberMax = 20, // limit number of requests (needed only for demo)
        xhr = redips.xhr;
    // if server is asked less than numberMax
    if (redips.number < numberMax) {
        xhr.open('GET', 'ajax-progress-bar.php', true); // open asynchronus request
        xhr.onreadystatechange = redips.requestHandler; // set request handler
        xhr.send(null);                                    // send request
        redips.number++;                                // increase counter
    }
    // otherwise stop polling
    else {
        redips.pollingStop();
    }
};

// request handler
redips.requestHandler = function () {
    let xhr = redips.xhr;
    // if operation is completed (readyState === 4)
    if (xhr.readyState === XMLHttpRequest.DONE) {
        // if HTTP status is OK
        if (xhr.status === 200) {
            // get progress level from JSON output
            let level = JSON.parse(xhr.responseText).progress;
            // set progress bar width and innerHTML
            redips.div.style.width = redips.div.innerHTML = level + '%';
        }
        // if request status is not OK
        else {
            redips.div.style.width = '100%';
            redips.div.innerHTML = 'Error:[' + xhr.status + ']' + xhr.statusText;
        }
    }
};

// button start
redips.pollingStart = function () {
    if (!redips.intervalID) {
        // reset number of requests
        redips.number = 0;
        // start polling
        redips.intervalID = window.setInterval(redips.sendRequest, 1000);
    }
};

// button stop
redips.pollingStop = function () {
    let xhr = redips.xhr;
    // abort current request if status is 1, 2, 3
    // 0: request not initialized
    // 1: server connection established
    // 2: request received
    // 3: processing request
    // 4: request finished and response is ready
    if (xhr.readyState > 0 && xhr.readyState < 4) {
        xhr.abort();
    }
    window.clearInterval(redips.intervalID);
    redips.intervalID = false;
    // display 'Demo stopped'
    redips.div.style.width = '100%';
    redips.div.innerHTML = 'Demo stopped';
};

// add onload event listener
if (window.addEventListener) {
    window.addEventListener('load', redips.init, false);
}
else if (window.attachEvent) {
    window.attachEvent('onload', redips.init);
}

Here you can see ajax-progress-bar.php source (how job progress is emulated). In your case you will have to calculate percentage of completion and return it in JSON format. Caching for ajax-progress-bar.php should be disabled. Many proxies and clients can be forced to disable caching with “header” lines pragma, cache-control and expires.

<?php
// no cache
header('Pragma: no-cache');
// HTTP/1.1
header('Cache-Control: no-cache, must-revalidate');
// date in past
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
// prepare demo progress value
$progress = (time() % 50) * 2;
// return progress level in JSON format
print "{\"progress\": \"$progress\"}";
?>

Download source code: redips6.tar.gz

11/21/2010 – Code tested with JSLint & enabled source download
06/10/2011 – Added limit of how many times to request the server (automatic demo stop)
26/03/2020 – Code tested with ESLint & changed to use JSON instead of XML

64 thoughts on “AJAX progress bar”

  1. @Sunny – Please, can you give me more details about a problem you mention. Actually, I made tests with Google Chrome 10.0.648.127, FireFox 3.6.10 and IE8 8.0.6001.18702 and in all browsers AJAX progress bar works. Unfortunately I don’t have Safari browser to confirm. This demo requests ajax-progress-bar.php every second and displays fetched progress value. Maybe you have some network problem (I’m not kidding) … I noticed that my other IE8 launched in virtual machine have similar issues. Can you manually access progress-bar.php and test received XML document? Next, try to reload that page and see how progress value is changing. You can also start DOM inspector, choose Network card and click “Start” button. AJAX JavaScript code will send requests and you should see each request line in inspector tool.

    Hope this tips will help you to find and solve the problem.
    Cheers!

  2. Is there a way to get the % finished from another script that is running? I have the page start a big script via xmlhttprequest (and I have a variable in that script defined as to what % it’s at), then can I use a 2nd instance of the xmlhttprequest (maybe to another script) to get the percent from the first script somehow?

  3. I would like to implement progress bar in classic asp. The progress bar will be included to indicate adding record to the database. A user can upload a file with up to 20,000 records. I would like to use this using classic asp. Any chance you can include demo code in asp?

  4. @Andy – I’m not sure or even it’s not possible to make direct communication between two browsers. But it’s possible indirectly. First page should somehow save job progress to the server and other instances should query for percentage. Actually, if you open two browser instances with this demo, both progress bars will show the same value. I know this answer comes late, but hopefully will be helpful to others.

    @ARA – I’m sorry, but my ASP skills are very thin so I can help you with server side only on conceptual level. If you want to create progress bar for database activity, my advice is to split database insert to several steps (simply create loop) . First make one insert with value 0 to the temporary table. When database insert starts, after every 200 inserted rows, update value in temporary table by 1. So, in case of 20,000 rows you will actually get job percentage completion. Now is easy to implement simple service to read updated value from database and return it as XML to the JavaScript progress bar. Hope this tip will give you an idea of how to create progress bar for adding record to the database.

  5. @ARA: You can communicate with asp through ajax.

    you can use response.write function.

    eg : response.write(“100”)

  6. @dbunic Your solution is exactly what I was thinking of. Its easy to store the progress in database and then send a request to some different page for fetching the progress value from DB with some interval and with a status flag to indicate when to stop sending Ajax request.

  7. There’s no need for XML (AJAX responseXML) in such a simple case, pure text is just fine here (AJAX responseText).

    What is ‘number_max’ variable in this code (?) may number of tries?

  8. @Stjepan – number_max is limit of how many times to request the server (this limit is needed only for this demo). You can see complete source in redips6.tar.gz package.

    You are right about XML but my intention was to show how to work with XML (for cases with more complex data returned by server) …

    Thanks for commenting or can I say “Hvala!” ;)

  9. Hi,
    I tried to use the progress bar inside a loop and it didn’t work. I got Error[0] inside the progress bar. My first assumption was that the request should be in synchronous mode, i.e.

    request.open(‘GET’, ‘ajax-progress-bar.php’, false);

    but in that case the progress bar updates only at the end of the loop. What am I missing? I just replaced the line

    $progress = (mktime() % 50) * 2;

    with the code below

    $progress = 0;
    $progressValue = 0;
    $progressMax = 10;
    while($progressValue < $progressMax) {
    	// Simulate a long process...
    	$start = mktime();
    	while($start + 1 > mktime()) {
    		$i++;
    	}
    	$progressValue++;
    	// Estimate progress in percent.
    	$progress = intval($progressValue / $progressMax * 100);
    }
    
  10. @Todor – The catch is that server side service should respond as soon as possible on client requests. What that means? I will try to explain on example will long/heavily database job. If you want to display job progress for say 1,000,000 inserts, JavaScript client should not ask main PHP task because script will not be able to reply when request comes. Instead, main task (PHP script which executes SQL inserts) can create some kind of log activity each 1000 inserts – to the text file or update some row in another table. Additional service can read this info and reply on AJAX requests very fast. So, similar principle can be applied to your case.

  11. @dbunic – Thanks a lot for your advice! It is a good idea to use a file or SQL table for the progress value. Is it also possible to use sockets as a mechanism for interprocess communication instead of file or SQL?
    By the way, I found the progress bar very usefull.

  12. @Todor – Thanks. I’m not sure what you mean by “using sockets” but for reading progress via AJAX it’s needed have web service. Web service (PHP, ASPX, JSP …) will reply to AJAX requests fired from JavaScript. Server side script can read current progress value from separate file, table… So, on the server side you are free to implement reading progress value on the way that will fit your case the best (even with using sockets if needed).

  13. @dbunic,

    I love your script.

    Is there anyway to load your script automatically without having to click on the “START” button?

    What i’m trying to do is, have your script display the progress while a datafile is being updated. I already got this to work perfectly. The only problem is I have to click on the ‘START’ button for your progress bar to execute.

    I moved your “onclick=”javascript:polling_start()” from the input-submit button to

    < body onload=”polling_start()” >

    but this doesn’t work. Thanks.

  14. Hi,

    how would you modify so that you dont have to click on the START button?

    I moved the polling_start() to the body onload, < body onload=”polling_start()” >

    but that didn’t work.

  15. @Jay – First sorry for delay. Yes, script can be called after page is loaded. You only have to add polling_start() line to the window.onload function:

    window.onload = function () {
        progress = document.getElementById('progress');
        request = initXMLHttpClient();
        polling_start();
    };
    

    The problem in your case was because you placed function call to the BODY onload and overwrite window.onload function. Hope this will solve the problem.

  16. Hi @dbunic,

    Sorry for the late reply, i was sidetracked on another project.

    Yours works perfectly after I added the polling_start().

    Thank you for such simple and easy to use scripts. The best one out there.

  17. @Jay – Thanks for the feedback and I’m glad my JS snippet was helpful. Cheers!

Leave a Comment