If you ask an average web-developer if it's possible to make a running progress bar on the web-page while it's loading(!), most of them will say "no". This is understandable as practically all web-pages do not exhibit such progress bars and are in a sense "static" - the whole web-site is dynamically generated from a database, but each page is static except for AJAX changes due to user actions. Here I will show how a lengthy PHP process can show a running progress bar while the page is loading. Of course, it doesn't make sense for pages loading within a second or three, but those import/export/dump/analyze/recode/generate scripts which takes ages.
The most trivial approach is to echo '.'; flush(); - it will display a dot for every loop iteration. Test this:
for ($i = 0; $i < 100; $i++) {
echo '.';
flush();
usleep(1000000*0.1); // total time 10 sec.
}
The output should be an increasing amount of dots progressively appearing on the screen during 10 seconds. We need to use flush(); in order to send the PHP output buffer out to the browser. Alternatively we could set ini_set('output_buffering', 0);
There is another trick we need to use sometimes, on some servers flush(); doesn't work alone - either Apache or browsers (particularly IE) are buffering the incoming HTML data as well. So I add echo str_repeat(' ', ini_get('output_buffering')); before flush(); to make sure the buffer is completely filled and being flushed automatically.
But we can improve the aesthetics of the output using some JavaScript. Remember, in the old days we were using write('Some HTML'); to generate some dynamic HTML by JavaScript while the page is loading(!)? So JavaScript can be executed immediately and not only onDOMReady.
So we build a progress bar using a container <div> and inside <div> of a different background color which will change it's width on the commands from JavaScript while the page in loading(!). Everything is encapsulated into a single class.
Usage example:
<?php
require_once 'nadlib/class.ProgressBar.php';
echo 'Starting…<br />';
$p = new ProgressBar();
echo '<div style="width: 300px;">';
$p->render();
echo '</div>';
for ($i = 0; $i < ($size = 100); $i++) {
$p->setProgressBarProgress($i*100/$size);
usleep(1000000*0.1);
}
$p->setProgressBarProgress(100);
echo 'Done.<br />';
Download class: http://pastebin.com/KSxjC01r
I hope using this technique will make your waiting more pleasant. You can even spot potential problems if the progress bar stops for unusually long time or is going slower and slower. I once improved the performance by adding a database index after watching the progress bar behavior.
Disclaimer: this idea is inspired by TYPO3 Extension Manager.
The most trivial approach is to echo '.'; flush(); - it will display a dot for every loop iteration. Test this:
for ($i = 0; $i < 100; $i++) {
echo '.';
flush();
usleep(1000000*0.1); // total time 10 sec.
}
The output should be an increasing amount of dots progressively appearing on the screen during 10 seconds. We need to use flush(); in order to send the PHP output buffer out to the browser. Alternatively we could set ini_set('output_buffering', 0);
There is another trick we need to use sometimes, on some servers flush(); doesn't work alone - either Apache or browsers (particularly IE) are buffering the incoming HTML data as well. So I add echo str_repeat(' ', ini_get('output_buffering')); before flush(); to make sure the buffer is completely filled and being flushed automatically.
But we can improve the aesthetics of the output using some JavaScript. Remember, in the old days we were using write('Some HTML'); to generate some dynamic HTML by JavaScript while the page is loading(!)? So JavaScript can be executed immediately and not only onDOMReady.
So we build a progress bar using a container <div> and inside <div> of a different background color which will change it's width on the commands from JavaScript while the page in loading(!). Everything is encapsulated into a single class.
Usage example:
<?php
require_once 'nadlib/class.ProgressBar.php';
echo 'Starting…<br />';
$p = new ProgressBar();
echo '<div style="width: 300px;">';
$p->render();
echo '</div>';
for ($i = 0; $i < ($size = 100); $i++) {
$p->setProgressBarProgress($i*100/$size);
usleep(1000000*0.1);
}
$p->setProgressBarProgress(100);
echo 'Done.<br />';
I hope using this technique will make your waiting more pleasant. You can even spot potential problems if the progress bar stops for unusually long time or is going slower and slower. I once improved the performance by adding a database index after watching the progress bar behavior.
Disclaimer: this idea is inspired by TYPO3 Extension Manager.
22 comments:
I found I had to add the following 2 lines in the class constructor in order that the progress bar would work
ob_end_clean();
ini_set('output_buffering', '0');
Great work!
Could you please post your code on Pastebin or somewhere else?
The link you have does not seem to be working.
Thanks!
Download link has been fixed.
Enjoy: http://pastebin.com/KSxjC01r
Thank you so very much, just what I needed.
I do have a question/remark, for some reason this progress bar does not work on Opera Mobile, do you know why? It seems the output buffer does not get flushed?
(it does work on Opera Mini, FireFox mobile, Android browser)
Sorry, not sure about Opera Mobile. Maybe it just defers JS execution to save CPU resources and battery? You can ask Opera developers.
Thank you for your reply,
I found out why this does not work in Opera Mobile, the browser has an extension called "Delayed Script Execution" which is standard On in Opera Mobile.
The setting can be found and changed by going to: opera:config#Extensions%7CDelayedScriptExecution
Oh wow.. by far one of the best progress bars class i've seen yet. Thanks for such great post.
I completely agree with the above comment (from jotorres1).
Very helpful, nice to tweak - all in all very educational post.
Thank you very much.
Excellent!! Thank you very much, this progress bar is awesome!!
However i've made some changes, instead of "for looping" i put the $p->setProgressBarProgress($x); inside a "while loop".
Example:
Inserting 1000 records.
For each mysqli insert statement, $x receives 0.1 (because 0.1 * 1000 records equals 100%).
So i have something like this:
$x = 0;
$num_records = 1000;
$calc = 100/$num_records;
while( $x < 100 )
{
// insert statement here
$p->setProgressBarProgress($x);
$x += $calc;
}
Where i have to add this code ....
Where i have to add this code ....
Great work.. absolutely brilliant.. have been searching for kinda progress bar for long time and found it now.
Thank you so much.
but this progress bar time is not dynamic na. is there any way to put a dynamic progress bar for page loading?
Hey Slawa this class is awesome! I need it to show some user feedback during a lengthy server process, and I put it to work with no pain!
I also needed to add a couple timers to show elapsed and (roughly guessed) remaining time. It's relatively accurate if a high number of iterations are involved.
Also added a switch to leave the progress bar on after 100% completion.
This link contains the patch for those two changes, in case somebody needs something similar and want to test/improve.
http://pastebin.com/8FLr1BB2
Thanks for your great job!
Great work :)
Please can u tell me how to use it in Cake php 1.3
Thanks.
Hi first of all thanks a lot it work perfect.
Do you know a way to use it more than one in the same code.
Thanks a lot Eduardo
I've modified it for my needs, use it like the original:
Multiple progress bars during long PHP script execution.
http://pastebin.com/g4JBZGcE
This is really awesome! Thank you so much for sharing your wonderful work!
Lori
can you help me solve the issue with 'Undefined variable':
Trying to use (understand this Progress bar) and i have got the result:
Starting…
Notice: Undefined variable: content in C:\xampp\htdocs\mcmtech\welcome-project\progress-bar\use-progress-bar.php on line 83
Done.
You can fixed it by removing the concatenate on the $content variable on its initialization.
Find this line
$content .=
replace with
$content =
Should get rid of the error hope this helps.
Post a Comment