AJAX progress bar

More...

With few lines of JavaScript and CSS you can make a simple AJAX progress bar. JavaScript will periodically ask for progress value and server will respond with XML. Progress value should be extracted from the XML and displayed as width of the DIV element. To start progress bar in this example, please click on the Start button and wait for a second to begin. If you have installed Firebug, open Net tab and watch how this page sends request to the Web server.

More precisely, every 1000ms send_request() function sends request to the server (see setInterval line in polling_start() function). ajax-progress-bar.php returns XML and request_handler() processes received XML. Progress value from XML has meaning of percentage completion of a job.

In the following JavaScript source, please focus to the sending and handling requests. Initialization of the XMLHttpRequest is written in the cross-browser manner and there's nothing more to add. The onreadystatechange property is a function that receives the feedback. It is important to note that the feedback function must be assigned before each send, because upon request completion the onreadystatechange property is reset. This is evident in the Mozilla and Firefox source.

// create XMLHttp request object in a cross-browser manner
initXMLHttpClient = function () {
    var XMLHTTP_IDS,
        xmlhttp,
        success = false,
        i;
    // Mozilla/Chrome/Safari/IE7+ (normal browsers)
    try {
        xmlhttp = new XMLHttpRequest();
    }
    // IE(?!)
    catch (e1) {
        XMLHTTP_IDS = [ 'MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0',
                        'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP' ];
        for (i = 0; i < XMLHTTP_IDS.length && !success; i++) {
            try {
                success = true;
                xmlhttp = new ActiveXObject(XMLHTTP_IDS[i]);
            }
            catch (e2) {}
        }
        if (!success) {
            throw new Error('Unable to create XMLHttpRequest!');
        }
    }
    return xmlhttp;
};

// send request to the server
send_request = function () {
    if (number < number_max) {
        request.open('GET', 'ajax-progress-bar.php', true); // open asynchronus request
        request.onreadystatechange = request_handler;       // set request handler
        request.send(null);                                 // send request
        number++;                                           // increase counter
    }
    else {
        polling_stop();
    }
};

// request handler (started from send_request)
request_handler = function () {
    var level;
    if (request.readyState === 4) {   // if state = 4 (operation is completed)
        if (request.status === 200) { // and the HTTP status is OK
            // get progress from the XML node and set progress bar width and innerHTML
            level = request.responseXML.getElementsByTagName('PROGRESS')[0].firstChild;
            progress.style.width = progress.innerHTML = level.nodeValue + '%';
        }
        else { // if request status is not OK
            progress.style.width = '100%';
            progress.innerHTML = 'Error:[' + request.status + ']' + request.statusText;
        }
    }
};

// button start
polling_start = function () {
    if (!intervalID) {
        // set initial value for current number of requests
        number = 0;
        // start polling
        intervalID = window.setInterval('send_request()', 1000);
    }
};

// button stop
polling_stop = function () {
    // 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 (0 < request.readyState && request.readyState < 4) {
        request.abort();
    }
    window.clearInterval(intervalID);
    intervalID = false;
    // display 'Demo stopped'
    progress.style.width = '100%';
    progress.innerHTML = 'Demo stopped';
};

Here you can see the source of ajax-progress-bar.php (how is job progress emulated). In your case you will have to calculate percentage of completion and return it in XML 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 the past
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
// define XML content type
header('Content-type: text/xml');
// print XML header
print '<?xml version="1.0"?>';
// prepare demo progress value
$progress = (mktime() % 50) * 2;
?>
<DOCUMENT><PROGRESS><?php print $progress ?></PROGRESS></DOCUMENT>

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)

This entry was posted on May 14, 2009 and is filed under JavaScript

Related posts

60 Responses to AJAX progress bar

  1. dbunic says:

    @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. gamezat says:

    thank you it's nice
    i will install it to server to server script
    thank you

  3. Andy says:

    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?

  4. ARA says:

    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?

  5. dbunic says:

    @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.

  6. Godly Mathew says:

    @ARA: You can communicate with asp through ajax.

    you can use response.write function.

    eg : response.write("100")

  7. netrevoltech says:

    @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.

  8. lingesh says:

    Good script! I also have one more package for AJAX upload (progress bar available) with server side functions to insert uploaded data into database .

    Download it here http://pigeoninfotech.com/php-ajax-uploader/

  9. dunt says:

    thank for share your knowledge :D

  10. Stjepan says:

    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?

  11. dbunic says:

    @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!" ;)

  12. Todor says:

    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);
    }
    
  13. dbunic says:

    @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.

  14. Todor says:

    @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.

  15. dbunic says:

    @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).

  16. Jay says:

    @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.

  17. Jay says:

    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.

  18. dbunic says:

    @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.

  19. Jay says:

    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.

  20. dbunic says:

    @Jay - Thanks for the feedback and I'm glad my JS snippet was helpful. Cheers!

Leave a Reply

Your email address will not be published. Required fields are marked *

*

In case of posting HTML tags or JavaScript code please convert special characters to HTML entities.
Especially pay attention to convert "<" character to "&lt;" entity!