How to build an ajax progress bar with jQuery and PHP

Written by Anthony Karassavov on Fri, May 7th 2010, 00:00 in Javascript and PHP

In many cases, especially when you are developing an e-commerce system or a CMS, you will have to deal with data import and/or export. This process usually involves looping through a large file (CSV, XML, JSON, etc.) and processing each "node" in some way. Dealing with this in a web environment could be tricky and you'll have to be particularly careful when building the user interface for the purpose.

Abstract

There is a live demo of the progress bar we're building, so those of you who can't wait can give it a try right away.

I want my script to start working on a file and at the same time return information to the browser on a regular basis (say each second) about what portion of the process has already been completed and what portion still remains to be processed. This is impossible with PHP alone, so here is how I figured it out.

I know it may sound confusing at first, but the idea is actually pretty simple and the code you’re about to see is quite straightforward.

Setup

Let's create an entry point for the script and call it index.php. This will contain our submission form. For this example, there will be no file to upload and process but rather a dummy loop. Take in mind that you cannot upload files to the server via ajax calls, so you might want to upload the file first and then redirect the user to your entry point.

<?php
// file: index.php
    session_start();
    $_SESSION['percent']=0;
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>Testing ajax progress bar</title>
    <link rel="stylesheet" href="style.css" media="screen" />
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
    <script type="text/javascript" src="script.js"></script>
</head>
<body>
    
    <h1><a href="#" id="start">Click</a> to go!</h1>
    
    <div id="response"><div></div></div>
    <div id="text"><img src="ajax-loader.gif" id="loader" /> </div>

</body>
</html>

Here is the process.php file which (as I already mentioned) will act as a dummy data file as well.

<?php
// file: process.php
    session_start();
    $_SESSION['percent'] = 0;
    $total_loops = 40;
    
    for ($i = 0; $i <= $total_loops; $i++) {
        $percent = ($i / $total_loops) * 100;
        usleep(50000); // pretend to be working on something
        session_start();
        $_SESSION['percent'] = number_format($percent, 0, '', ''); // update session var
        session_commit();
    }
?>

Pretty straightforward, eh? We've initialized a session and set the total number of loops to 40. After that I start looping through an "imaginary file" and instead of doing something meaningful, I sleep for half a second. At each iteration I write to a session variable, which I will read with the following script later on:

<?php
// file: display.php
    session_start();
    echo $_SESSION['percent'];
?>

This file does nothing but return the session variable to index.php. Finally there's the heart of the script, which connects everything together - script.js.

$(document).ready(function(){
    $('#start').click(function(){
        $('#text').append('Working...');
        $('#response').css('display','block');
        $.ajax({
            cache: false,
            type: 'post',
            url: 'process.php',
            beforeSend: start_display()
        });
    });
});

var interval='';
    
function start_display() {
    if (interval=="") {
        interval=window.setInterval("display()",200);
        $('#loader').css('display','inline');
    } else {
        stop_display();
    }
}

function stop_display() {
    if (interval!="") {
        window.clearInterval(interval);
        interval="";
    }
}

function display() {
    $.ajax({
        cache: false,
        type: 'get',
        url: 'display.php',
        success: function(response) {
            $('#response div').css('width',response+'%');
            if (response=='100') {
                stop_display();
                $('#text').html('Done!');
            }
        }
    });
}

OK, let's have a look at what this JavaScript file does. First, there's a function attached to the #start button onclick event. It makes an ajax call to process.php and before that it executes start_display() function. The start_display() function calls another function - display(), which is called regularly each 200 milliseconds. The display() function's purpose is to call display.php and get information from that session variable we wrote to in process.php. It also changes the width of our progress bar according to the value returned from $_SESSION['percent']. Once it reaches 100 percent, the function calls stop_display(), which clears the interval we've set in start_display().

And this is pretty much all of it. Check out the live demo and tell me what you think in the comments section below.

sessionmultiple ajax requestsscriptphpprogress barajaxjquery

1 week ago Popcorn said:
Nice one!thank you! Reply
on 15/7/10 tonie said:
Glad to see you find it useful. Thanks :) Reply
on 7/7/10 Gorazd said:
Thnx, you save my day.
I own you a beer. Reply
on 9/6/10 tonie said:
Well, to tell you the truth, this way of solving the progress indication problem with PHP ain't one of the most efficient now is it? What I mean is, that sending a request to your web server every half a second in order to inform your users about the progress they've made on a file... ...well it just doesn't feel right.



Unfortunately, PHP's limitations can force you to think of inefficient workarounds for such kind of problems. Your problem with IE could be the slower JavaScript engine that it implements, but that's just a guess. Keep me updated on your findings, if you don't mind of course.



Cheers :) Reply
on 7/6/10 JB said:
Ok great! Thanks! So this still looks like a very possible solution for me. At the moment i am using it to monitor the generation of PDF files. I seem to have some kind of problem with Internet Explorer tho - it seems to be slower than in firefox and is more likely to cause a maximum execution time error in the PHP scripts. No idea why this is at the moment because the processing is PHP on the server and should have nothing to do with the browser. Hmmm i guess some investigation is needed... Determined to give my users some visual feedback while i generate. Reply
on 3/6/10 tonie said:
Hi JB,
Thanks for being more observational than I am. Actually it works quite well. The problem in the demo is that I used some mozilla/webkit only CSS rules to style the progress bar and so IE has a problem visualizing the background. I fixed the CSS and it displays it properly now. Reply
on 2/6/10 JB said:
Hi
Really nice solution to a nasty problem. Just spent a while implementing it on a project i'm working on. One question tho'. Can it be it doesn't work on Internet Explorer 8? Reply

tell me what you think

If you liked the article and want to contribute to it, please feel free to leave your comment. HTML tags are not allowed, but you can use the following BBCode to enhance your message: [url] [quote] [code] [b] [i] [u] [color].

Get the comment feed
share
Toggle panel

So who is anthony anyway?

i am anthony - an all-around web developer with a degree in business. I like to code functional websites with beautiful interfaces. PHP, javascript and espresso is a powerful combination.

Lately

Your voice

The usual stuff

i am anthony

Thoughts and notes from a web developer

muxcmux: