Page 1 of 1

A Progress Bar (Javascript Required)

Posted: Tue Sep 11, 2007 5:36 am
by CoderGoblin
I have recently come across the requirement to import data from one system to another. One of the main problems with importing the data is the lack of information indicating progress. Sure I can print out dots and a new line after 80 dots but really wanted something neater. Using CSS for bar charts I produced the following.

Code: Select all

<?php
/**
 * Define a couple of functions here..
 * 
 * showStatus is used to display text messages
 * updateBar is used to show the progress bar
 */
function showStatus($msg)
{
        echo "    <script language=\"JavaScript\">
    <!--
       updateMsgBox(\"".$msg."\");
    //-->
    </script>";
        flush();
}

function updateBar($percent)
{
  $msg='<div class=\"progbar\"><strong class=\"bar\" style=\"width: '.$percent.'%;\">'.$percent.'%</strong></div>';
  showStatus($msg);
}
?>
<html>
  <head>
    <title>Dummy Progress Bar</title>
    <style>
    .progbar { 
        position: relative; /* IE is dumb */
        width: 99%; 
        border: 1px solid #B1D632; 
        padding: 2px; 
    }
    .progbar .bar { 
        display: block;
        position: relative;
        background: #B1D632; 
        text-align: center; 
        color: #333; 
        height: 2em; 
        line-height: 2em;            
    }
    .progbar .bar span { position: absolute; left: 1em; }
    
    .msgbox {
    	position: absolute;
        top: 40%; left: 35%;
        width: 40%;
        height: 40px;
        filter:alpha(opacity=80);
        -moz-opacity:80%;
        margin:0;
        padding:15px;
        background-color:#eee;
        border:1px solid #333;
        text-align: center;   
        z-index:1000;
        display:none;
        cursor:default;
    }
    </style>
    <script language="javascript">
    <!--
    function getPageItem(itemID)
    {
        if (document.getElementById) return document.getElementById(itemID);
        if (document.all) return document.all[itemID];
        return null;
    }
    
    function updateMsgBox(newHtml)
    {
        var span_obj = getPageItem("msgbox");
        if (span_obj) {
          if (span_obj.style.display != "inline") {
            span_obj.style.display = "inline";
          }
          span_obj.innerHTML = newHtml;
        }
    }
    
    function killMsgBox()
    {
        var span_obj = getPageItem("msgbox");
        if (span_obj) span_obj.style.display = "none";
    }
    -->
    </script>
  </head>   
  <body>    
    <div id="msgbox" class="msgbox"></div>

<?php

/**
 * The main processing block. This could be getting information from a database or whatever.
 * Important thing to realise is that each call to updateBar increases the amount of 
 * information sent from the server to client so here we ensure only 100 calls are made.
 * The precise method of when you send information would depend on what you are doing.
 */ 
    showStatus("Getting Information");
    foreach(range(1,100) as $count) {
      updateBar($count);
      usleep((rand(1,20)*10000));
    }
    showStatus("Producing Output");
?>    
    <script language="javascript">
      <!--
         killMsgBox();
      //-->
    </script>
    <p>This is the results or whatever</p>
  </body>
</html>
some things to note...
1) Not usable for File Uploads.
2) Templating cannot work with this. It needs to output the html head info, start processing and output as it goes.
3) Although stated in the code please note ... Important thing to realise is that each call to updateBar increases the amount of information sent from the server to client. Just do a view source when complete to see what I mean.
4) Javascript is required. I don't like releasing things that require javascript but for the import I use I can guarantee javascript will be enabled as the import will only be run internally (normally by myself).
5) This is only a quick "demo" but if anyone has constructive ideas please feel free to blast it.

Posted: Tue Sep 11, 2007 12:29 pm
by pickle
If this is working for you, and you built it for you, then there's no real motivation to change it...

However, what about an AJAX solution? What if you get the transfer script to write to a file with the % done after every file transfer. Then you could use AJAX every 2, 3, or whatever seconds to access the file & update the progress bar on the fly. That would reduce the amount of traffic between the server & the client.

Posted: Wed Sep 12, 2007 3:32 am
by CoderGoblin
Thanks for the reply pickle. Thought about an ajax solution but decided against it for a couple of reasons. If it's querying the server every 2-3 seconds it may result in more traffic depending on what the 'loop' is. Also because of the reason for building this (quick visible progress indicator for internal use) it seemed to just add an extra layer of complexity to something that I want to implement in any code quite quickly. Thirdly the loop I was building would be quicker in one file processing a couple of arrays rather than trying to "split" out the main loop.