Loading...


bookmark - Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers!

Image Preloader With Progress Bar Status - Pure Client-Side JavaScript tested in 4 Browsers!

 
 Discussion by SystemWisdom with 30 Replies.
 Last Update: December 18, 2009, 9:51 am ( View Rated (2) )
 
bookmark - Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers!  
Quickly Post to Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers! w/o signup Share Info about Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers! using Facebook, Twitter etc. email your friend about Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers! Print
Reply / Comment New Discussion / Topic Share / Bookmark E-Mail a Friend Print

Tutorial: Image Preloader with Progress Bar, by Rob J. Secord, B.Sc. (SystemWisdom)


Description:
A Tutorial for a Client-Side Image Preloader with Dynamic Real-Time Progress Bar Indicator written in JavaScript!
Tested to work with 4 Major Internet Browsers: Firefox, MSIE, Netscape, Opera

(Complete sample solution provided at end of tutorial, just put it on your web-server, add your images and go!)


Intended Audience:
Beginner to Intermediate Web Developers.

Although this tutorial will cover some advanced aspects of JavaScript, I will try to explain it all as thoroughly as possible. At the same time, I will keep the functionality basic and leave room for you to expand on it as you see fit.

Assumed knowledge:
-- OOP Concepts such as Classes
-- Arrays, Loops
-- JavaScript Basics
-- Working with *.js files


Background:
Many websites rely on Images for the Graphical User Interface (GUI) of their Web Applications. This is great for visual appeal, but when the site consists of mostly images (both large and small) to make up the entire layout, then your visitors download time ends up taking longer. And as all of us should already know, no one likes to wait too long for a site to load without at least a visual indication of its progress.
On average, people with broadband connections will wait up to 10 seconds for a site to load -- which isn't very much time at all!

Another problem while the site is loading for the visitor, is that they see the site slowly take shape, as single images are downloaded individually and fitted into the screen so as to form the layout. It would be much more appealing if the site were to load all at once, images and text loaded and in place all ready to go!

The solution? An Image Preloader with Progress Bar Status!

Great! But how?


Theory:
Utilizing the Document Object Model (DOM) of HTML with the power of JavaScript, we can access all of our sites images as Objects, giving us access to the Events fired by the Image Objects.

On top of that, when we load an Image in JavaScript via the DOM, we point to the location of the Image via a Uniform Resource Locator (URL) just as we would with regular HTML <IMG> tags. This means that the Image gets loaded into the memory of the visitors computer. Now, when we call for an image in our main page like <img src="img/blah.gif">, the relative Image URL is already loaded, and thus the Image gets displayed immediately.

Now as I mentioned before, when we load an Image via JavaScript and the DOM, we have access to the Events of that Image Object, and to name a few of those events we have:

OnLoad()
OnError()
OnAbort()

Pretty straight-forward really; when the Image is completely loaded the OnLoad() event gets called. If an error occurs while loading the Image the OnError() event gets called. And lastly, if the user (visitor) leaves the page (or closes browser) the OnAbort() event gets called.

In this tutorial we will only need to deal with the OnLoad() event, however, the other events could be of great use, and should be simple enough for you to implement on your own after reading this tutorial. (I will leave a basic code structure for the extra events in the sample at the end of this tutorial, but I will not be explaining them in great detail. That could be left open for discussion!)

The main purpose of implementing the OnLoad() event is to keep track of our loaded images so we can display a Real-Time Progress Bar to our users. The idea here is to keep a counter of how many images have been loaded, so we know our progress. (Hint: You could also track how many Images fail to load via the OnError() event!)

Finally, we have to display that progress on the screen, which can be solved with the use of Span Tags and CSS! In this tutorial I will simply lay out 10 boxes (via Span Tags) each with a Grey BackGround Style, and as the progress increases, I will change the style of each consecutive box to a Blue BackGround, giving a visual appearance of a Progress Bar!

I hope you were able to follow along thus far, we are going to tackle the actual code next! :D


Implementation:
All right! We are going to start on the last section of this tutorial where we actually get to build something! (If you feel daunted by the length of this tutorial at this point, know that the longest section lies yet ahead! Fear not, brave reader!)

Now, I am going to take you through 3 stages in building our Image Preloader with Progress Bar:

1) Writing the Preloader class in JavaScript -- This is the core of our preloader, and where most of the theory from above takes place;
2) Writing the Progress Bar Page -- This is a simple HTML page used soley to display the Progress Bar to our visitors;
3) Putting it all together -- This is where we will implement the Preloader Class into our Progress Bar Page to make it all work!

Due to the 'intertwined' nature of the code, it will be difficult to explain, and you may benefit from reading parts of this section more than once after having read it all. Try to remember any parts that confuse you, and come back to it later, after reading more of the tutorial.



Writing the Preloader class in JavaScript:
We start off by making a new file called 'ipreload.js'. This will be our Image Preloader Class File which will contain a single class declaration. If you have never worked with Classes in JavaScript before, they may seem odd at first. This is due to the fact that JavaScript (ECMAScript) lacks full support for user-defined class types. :D But fret not, my friend! I shall explain them anyway! :D

(Note: If you already understand classes in JS, you may safely skip the following quoted section)

QUOTE (On the Subject of JavaScript Classes)

Classes in JavaScript are declared using the 'function' keyword. Weird? This is because the declaration is used as both the Declaration and the Constructor. All of your member variables to be used in your class should be declared within this Declaration/Constructor using the keyword 'this' followed immedialtely by a dot and then the variable name.  Assignment operations may also take place within the statement.  A full example thus far:

CODE


function MyClass()
{
   this.MyVariable = 0;
}


Now, to add methods (or member functions) to the class gets weirder with the involvment of the 'prototype' keyword, as in:

CODE


MyClass.prototype.MyFunction = function()
{
   // Access to the member variable:
   this.MyVariable = 1;
}


There you go, a working Class in JavaScript with 1 Member Variable, and 1 Member Function!
Now to instantiate an object of that class is as simple as calling the Constructor function with the 'new' keyword, and assigning its value to a variable, as in:

CODE


var MyObject = new MyClass();

// Call the member function:
MyObject.MyFunction();


I hope you were able understood that, please mind my briefness as this is not a tutorial on Classes in JavaScript (that could be an entire tutorial of its own!)


Okay, moving on.. Now that we understand classes in JavaScript, we will begin with our 'ImagePreload' Class. I will first show you the completed class and then I will explain how it works, this way you will have reference to what I am writing about.

File = ipreload.js:

CODE


<!--

function ImagePreload( p_aImages, p_pfnPercent, p_pfnFinished )
{  
   // Call-back functions
   this.m_pfnPercent = p_pfnPercent;
   this.m_pfnFinished = p_pfnFinished;

   // Class Member Vars
   this.m_nLoaded = 0;
   this.m_nProcessed = 0;
   this.m_aImages = new Array;
   this.m_nICount = p_aImages.length;

   // Preload Array of Images
   for( var i = 0; i < p_aImages.length; i++ )
       this.Preload( p_aImages[i] );
}

ImagePreload.prototype.Preload = function( p_oImage )
{  
   var oImage = new Image;
   this.m_aImages.push( oImage );

   oImage.onload = ImagePreload.prototype.OnLoad;

   oImage.oImagePreload = this;  // Give the Image Object a Reference to our Class..
   oImage.bLoaded = false;       // Custom value added to track state
   oImage.source = p_oImage;     // Original Source Path to Image (for later use)
   oImage.src = p_oImage;        // Image Object Source Path
}

ImagePreload.prototype.OnComplete = function()
{  
   this.m_nProcessed++;
   if ( this.m_nProcessed == this.m_nICount )
       this.m_pfnFinished();
   else
       this.m_pfnPercent( Math.round( (this.m_nProcessed / this.m_nICount) * 10 ) );
}

ImagePreload.prototype.OnLoad = function()
{  
   // 'this' pointer points to oImage Object because this function was previously assigned
   // as an event-handler for the oImage Object.
   this.bLoaded = true;
   this.oImagePreload.m_nLoaded++; // Access our Class with the Reference assigned previously..
   this.oImagePreload.OnComplete();
}

//-->


Okay, we have a class called 'ImagePreload' whose constuctor takes 3 parameters, namely p_aImages, p_pfnPercent and p_pfnFinished respectively.
The first parameter 'p_aImages' is an Array of Strings which holds the URL of all of the images we want to Preload. We will build this array outside of this class later on, and pass it into the class when we instantiate an object of the class.
The second and third parameters are actually Functions (or more precisely, Function Pointers) that we want our class to have access to without being part of the class itself. We will deal more with these later.

Now looking into the Constructor function for our class we will see that the last two parameters are being assigned to Member Variables of the class, so that they are stored locally and are available for the class to call as functions later on. They are commented as "Call-Back functions", and that is exactly what they are: They "Call-Back" to the code that "Called" our Class' Constructor/Member Functions. More over, they can be considered Events that are raised by our class. I will elaborate on this further later in this tutorial. Moving on..

Next you will see some more Member Variables being declared within the class, namely: m_nLoaded, m_nProcessed, m_aImages and m_nICount. I will try to explain them each briefly, yet adequately each in a single paragraph of their own;

Since our class will keep a counter for all of the Images it loads, we will need a Member Variable to hold that counter, starting at 0. The 'm_nLoaded' Member Variable serves this purpose. After each successfully loaded image, 'm_nLoaded' will be incremented by 1.

Our class will also keep a counter for the number of Processed Images (meaning the images that our Progress Bar has accounted for) to calculate the Overall Progress. The 'm_nProcessed' Member Variable serves this purpose. This should be unaffected by Image Load Failures from the OnError() events (Be sure to note that if you plan to add error handling to this class).

Our class will also be storing the Image Objects in Memory on the visitors machine, so that they are considered "Preloaded", and we will do this by creating an Array, and adding the Image Objects to the Array as the images are loaded. The 'm_aImages' Member Variable serves this purpose. Note that this Member variable starts with 'm_' unlike the parameter with the same name, which starts with 'p_'.

Finally, our class will need to know the total number of images it is preloading in order to compute a progress, and yes, the 'm_nICount' Member Variable serves this purpose! We can get this value from the Arrays 'length' property, which returns the number of elements in the array.

Okay, so that should explain most of the Constructor function to you, and now we will look at the last part of the code in the constructor function.. Hopefully, you've kept up with me and the reference code above, if not here is the part I am refering to next:

CODE


   // Preload Array of Images
   for( var i = 0; i < p_aImages.length; i++ )
       this.Preload( p_aImages[i] );


This is a basic FOR loop which we will use to walk through the Array of Image URLs that were passed into the function as the First Parameter. We will then pass each individual URL string to our class' Preload() Member Function which handles the actual "Preloading" of each Image Object.
As you can see by now, as soon as you Instantiate the ImagePreload Object passing it the Array of Image URLs, the preload process will start immediately, and within seconds (hopefully) all of your Images will be preloaded!

But wait! What about the progress bar?!
Since the ImagePreload class will be taking care of loading all of those images, how will the Progress Bar know what's going on?

This is solved by those two "Call-Back" functions I mentioned earlier. Since the Progress Bar itself will be completely seperate code with a seperate responsibility, it will have two functions of its own. One function will update the Progress Bar Display, and the other function will redirect to the main page of your site when the preloading is done!

Now, as I mentioned earlier, the two "Call-Back" functions were assigned to Member Variables which means that any function in our class can now call either of those two functions whenever it needs to. Basically, we will use them as Events, so that everytime an image is loaded, the progress is calculated and the Event (Call-Back Function) is called, sending back the current progress! Now since those functions actually belong to (and are coded in) the Progress Bar, then they will get called and Instantly Update the Progress or Redirect the Page Accordingly!

Hopefully that made sense to you, either way, I shall explain it in more detail.
In reference to the code above, we should now be looking at the Preload() function declared as:

CODE


ImagePreload.prototype.Preload = function( p_oImage )


Recall that this function is executed in the FOR loop in the Constructor of our Class, and is being passed a URL string of the Image to Preload. Looking into the function we notice the first two statements, the first line just instantiates an Image Object (which is part of JavaScript) and the second line adds that Image Object into our Array of Image Objects which we had declared earlier in the Constructor. Memory space is now reserved for this image on the visitors computer!

Next, we see a see a statement dealing with the OnLoad() event of the Image object:

CODE


oImage.onload = ImagePreload.prototype.OnLoad;


We are basically telling the Image Object to Call a Member Function in our class when the Image is loaded, and our Member Function will be responsible for tracking the progress.
But there is one tricky thing to note about this assignment; the 'ImagePreload.prototype.OnLoad' function now "belongs" to the Image Object, even though the function is implemented in our class. The important point is that the 'this' keyword within our 'ImagePreload.prototype.OnLoad' Member Function will not point to our 'ImagePreload' Object Instance, but rather the Image Object Instance. (Crude Analogy: this is not this anymore, it is that! -_-)
This may seem very confusing at first, but don't give up! It makes sense! Besides, you don't have to understand it, just know that it works and how to use it!

Now if you didn't understand what I just wrote, then the next statement won't make much sense to you:

CODE


oImage.oImagePreload = this;  // Give the Image Object a Reference to our Class..


Basically, we want the Image Object to have access to our Class. This is used to access the Call-Back functions in our class from within the OnLoad() function which now belongs to the Image Object. Make sense? I hope so, I don't know how else to explain it! :D But don't fret my friend, read further! Maybe it will clear up for you later!)

The next few statements are much simpler:

CODE


   oImage.bLoaded = false;       // Custom value added to track state
   oImage.source = p_oImage;     // Original Source Path to Image (for later use)
   oImage.src = p_oImage;        // Image Object Source Path


The first is just a simple boolean flag to indicate whether the Image Object has been loaded or not, which is set to False by default.
The next two lines are pretty much the same, except that the first one is a custom variable and the second one is actually an Image Object Property 'src', much like in HTML <IMG> Tags. When you assign a a URL to this property it will be interpreted immediately and the actual image will be loaded into the Memory space we had reserved earlier! This is the load process internally, and when then image is done loading the OnLoad() event gets fired calling our OnLoad() Member Function.
The custom variable 'source' is used for referencing the original path & filename of the image (if you should need to) whenever you need to.

And that is the Preload() function!! Hopefully, you now understand how the actual Preload process works!

We will now move on to the final 2 Member Functions: OnLoad and OnComplete..

CODE


ImagePreload.prototype.OnLoad = function()
{  
   // 'this' pointer points to oImage Object because this function was previously assigned
   // as an event-handler for the oImage Object.
   this.bLoaded = true;
   this.oImagePreload.m_nLoaded++; // Access our Class with the Reference assigned previously..
   this.oImagePreload.OnComplete();
}


Now, since the OnLoad() function belongs to the Image Object, we can directly manipulate the variables we gave it earlier, namely the 'bLoaded' boolean flag. We will set this to true, to indicate that this image object was loaded successfully.
We also need to increment the counter holding the total number of Loaded Images thus far, which is a Member Variable that belongs to the ImagePreload class, and this function doesn't! So, we access the reference we stored earlier as a custom variable in the Image Object. We can now use that reference object to access anything in our ImagePreload class, which the next two statements do; the first of which simple increases the counter, and the second calls our final Member Function 'OnComplete'.

CODE


ImagePreload.prototype.OnComplete = function()
{  
   this.m_nProcessed++;
   if ( this.m_nProcessed == this.m_nICount )
       this.m_pfnFinished();
   else
       this.m_pfnPercent( Math.round( (this.m_nProcessed / this.m_nICount) * 10 ) );
}


This function is pretty simple, it belongs to our ImagePreload class and simply increments the counter of Processed Images. (Note: If the # of Processed Images is not equal to the # of loaded images, then some of your imges fail to load. But the progress will still compute correctly!)

Next it checks to see if it has processed all the required images, and if so it raises the "Finished" event, which tells the Progress Bar code to redirect to the main page. However, if it is not done loading all of the required images, it will raise the "Percent" event, which tells the Progress Bar to update the display according to the Percent Complete!
(The Percentage is rounded to a single digit number from 1-10, but this could easily be changed to suit your needs).

Whew! Take a break! Your done Stage 1 of the Implementation Section! It's pretty much downhill from here! :D


Writing the Progress Bar Page:
I am going to keep this part of the tutorial a simple as possible, since it only deals with basic HTML tags, and some simple CSS styles for appearance. This is going to be the GUI of the Progress Bar, and most of you will probably change the appearance of it anyway.

Basically, you just need an empty webpage (preferably no images, otherwise, what's the point of the Preloader?)
So it could look something like this (keeping it simple):

File = index.html:

CODE


<html>
<head>
<style type="text/css"><!--

.OuterBorder
{  border:1px solid #426394;
  padding: 2px 5px 2px 5px;
}
.FullDot
{  border:1px solid #426394;
  background-color:#DAE1EB;
  cursor:default;
}
.EmptyDot
{  border:1px solid #426394;
  background-color:#F3F6FA;
  cursor:default;
}
//--></style>
</head>
<body>

<center>
<table cellpadding="0" cellspacing="0" border="0" class="OuterBorder">
 <tr>
  <td>
     <span id="sDot1" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot2" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot3" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot4" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot5" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot6" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot7" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot8" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot9" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot10" class="EmptyDot">&nbsp;&nbsp;</span>
  </td>
 </tr>
</table>
</center>

</body>
</html>


You'll notice it is just 10 span tags within a bordered table, each span tag representing an 'EmptyDot'. As the progress increases, the 'EmptyDots' will become 'FullDots'.

Well, nothing special there, just some regular HTML/CSS stuff.. So, we move on, to the Final Stage!


Putting it all together:
This stage shouldn't be too tricky either.. First we will have to include or ImagePreload class file into our HTML page, then we will write our 2 functions that will respond to the events raised by our ImagePreload class, as well as a third function to start the whole Loading process rolling! Also, we will add an extra link on the page, that will give the visitor the option to "Skip Preload" and go straight to the main page..

First, including the Preloader, which most of you I assume will be familiar with. Simple JS file including:

CODE


<script type="text/javascript" language="JavaScript" src="ipreload.js"></script>


Now, we want to create 3 more JavaScript functions inside the <HEAD> tag of the web page, as mentioned earlier. I will first show you the completed HTML file, and then like before, I will explain it all to you, with the code as reference:

File = index.html:

CODE


<html>
<head>
<style type="text/css"><!--

.OuterBorder
{  border:1px solid #426394;
  padding: 2px 5px 2px 5px;
}
.FullDot
{  border:1px solid #426394;
  background-color:#DAE1EB;
  cursor:default;
}
.EmptyDot
{  border:1px solid #426394;
  background-color:#F3F6FA;
  cursor:default;
}
//--></style>

<script type="text/javascript" language="JavaScript" src="ipreload.js"></script>

<script type="text/javascript" language="JavaScript"><!--

var g_iStep = 0;

function OnImgUpdate( iProgress )
{  
   if( (iProgress >= 1) && (iProgress <= 10) && (iProgress > g_iStep) )
   {  
       g_iStep++;
       var oSpan = document.getElementById( "sDot" + iProgress + "" );
       oSpan.className = 'FullDot';
   }
}

function OnCompletion()
{  
   document.location.replace('main.html');
}

function StartPreload()
{
   var szImages = new Array( "image1.gif", "image2.jpg", "image3.png", "etc..." );

   // Execute Image Preloader
   var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );
}

--></script>

</head>
<body onload="StartPreload()">

<center>
<table cellpadding="0" cellspacing="0" border="0" class="OuterBorder">
 <tr>
  <td>
     <span id="sDot1" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot2" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot3" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot4" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot5" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot6" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot7" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot8" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot9" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot10" class="EmptyDot">&nbsp;&nbsp;</span>
  </td>
 </tr>
</table>
</center>

</body>
</html>


Now, the first thing you'll notice, is that there is not much left to do! Yay! I'm exhausted from typing! Okay, going back on track now...

You could seperate the CSS and put it in an external *.css file if you prefer, but I kept this tutorial simple, with the use of only 2 files (plus the rest of your website).

The first thing to note in the newely added JavaScript code block is:

CODE


var g_iStep = 0;


This is a global variable that needs to maintain its value, hence it is declared outside of any specific code block. The reason for this variable is simple; The Progress Bar display in this tutorial only has 10 steps, but you could have 50+/- images. Since the Update Event gets called for every loaded image, then it is unnesseccary to Update the progress bar with every image, instead we track when the Percent value changes, store the new value in the 'g_iStep' variable and Update the display only if the 'g_iStep' variable has increased!

This all takes place in the 'OnImgUpdate' function:

CODE


function OnImgUpdate( iProgress )
{  
   if( (iProgress >= 1) && (iProgress <= 10) && (iProgress > g_iStep) )
   {  
       g_iStep++;
       var oSpan = document.getElementById( "sDot" + iProgress + "" );
       oSpan.className = 'FullDot';
   }
}


Should be pretty straight-forward from what I wrote above. Worth noting though, is the 'oSpan' variable, which refers to one of the HTML <SPAN> tags in the DOM that make up the Progress Bar Display. We can get access to this Span Tag as an Object via the 'document.getElementById()' function which is part of JavaScript. All we need to do, is tell the function the ID of the Span Tag, which we have named successively ranging from "sDot1" to "sDot10". Each ID corresponds to a Progress Interval. Now that we have access to the Span Tag Object, we can change its Style via the 'ClassName' property, which we then change to 'FullDot' so as to indicate a Full Dot, and Progress on the Progress Bar!

Well, that takes care of the Progress Bar actually moving! Now we have to Redirect to the Main page of your site once it is complete. Our ImagePreload class will take care of raising the Event when all of the images are loaded, we just need to do something now when that event occurs.

This will happen in our 'OnCompletion()' function:

CODE


function OnCompletion()
{  
   document.location.replace('main.html');
}


Pretty simple really, if you understand the Basics of Javascript. We just Redirect to the Main page of your site (assuming the page is called 'main.html', if not point it to your page). Also, the replace() function has the added benefit of removing the preloader from the history, so people don't click there Back button and go to a Preloader page!


And finally, where my tutorial draws near to its conclusion, we put it all together in one Simple function:

CODE


function StartPreload()
{
   var szImages = new Array( "image1.gif", "image2.jpg", "image3.png", "etc..." );

   // Execute Image Preloader
   var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );
}


Now the first thing you will notice, is the Array of Image URLs, these are "Relative" URLs and should point to the actual image files on your webserver that you wish to preload. This script will not work "As Is" unless you point to Your image files in the Array.

How ever you build that Array of images is up to you, I personally use PHP to recurse through my /img/ directory making a list of all the Image path/filenames it finds, and then outputs that list into the Preloader Pages Image URL Array above. That would be a tutorial of it's own, so I will save going into detail about that for another day!

Next, is the MAin line of execution for our Preloader script.

CODE


var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );


In this one line is where we Instantiate a new 'ImagePreload' Object, passing to its Constructor 3 parameters: the Array of Image URLs, the 'Update' Event Function Pointer and the 'Finished' Event Function Pointer which starts the whole Preloading process in motion!

The 'StartPreload' function should be called as soon as the Preload Page loads, so we will call it in the onload event handler of the Body Tag:

CODE


<body onload="StartPreload()">



And there you have it! An Image Preloader with Progress Bar Status in JavaScript!

So, as the End-Result, we have the 2 completed Files (with the extra stuff I mentioned left in):

File = ipreload.js:

CODE


<!--

function ImagePreload( p_aImages, p_pfnPercent, p_pfnFinished )
{   // Call-back routines
   this.m_pfnPercent = p_pfnPercent;
   this.m_pfnFinished = p_pfnFinished;

   // Class Member Vars
   this.m_nLoaded = 0;
   this.m_nProcessed = 0;
   this.m_aImages = new Array;
   this.m_nICount = p_aImages.length;

   // Preload Array of Images
   for( var i = 0; i < p_aImages.length; i++ )
       this.Preload( p_aImages[i] );
}

ImagePreload.prototype.Preload = function( p_oImage )
{   var oImage = new Image;
   this.m_aImages.push( oImage );

   oImage.onload = ImagePreload.prototype.OnLoad;
   oImage.onerror = ImagePreload.prototype.OnError;
   oImage.onabort = ImagePreload.prototype.OnAbort;

   oImage.oImagePreload = this;
   oImage.bLoaded = false;
   oImage.source = p_oImage;
   oImage.src = p_oImage;
}

ImagePreload.prototype.OnComplete = function()
{   this.m_nProcessed++;
   if ( this.m_nProcessed == this.m_nICount )
       this.m_pfnFinished();
   else
       this.m_pfnPercent( Math.round( (this.m_nProcessed / this.m_nICount) * 10 ) );
}

ImagePreload.prototype.OnLoad = function()
{   // 'this' pointer points to oImage Object
   this.bLoaded = true;
   this.oImagePreload.m_nLoaded++;
   this.oImagePreload.OnComplete();
}

ImagePreload.prototype.OnError = function()
{   // 'this' pointer points to oImage Object
   this.bError = true;
   this.oImagePreload.OnComplete();
}

ImagePreload.prototype.OnAbort = function()
{   // 'this' pointer points to oImage Object
   this.bAbort = true;
   this.oImagePreload.OnComplete();
}

//-->



File = index.html:

CODE


<html>
<head>
<style type="text/css"><!--

.OuterBorder
{  border:1px solid #426394;
  padding: 2px 5px 2px 5px;
}
.FullDot
{  border:1px solid #426394;
  background-color:#DAE1EB;
  cursor:default;
}
.EmptyDot
{  border:1px solid #426394;
  background-color:#F3F6FA;
  cursor:default;
}
//--></style>

<script type="text/javascript" language="JavaScript" src="ipreload.js"></script>

<script type="text/javascript" language="JavaScript"><!--

var g_iStep = 0;

function OnImgUpdate( iProgress )
{  
   if( (iProgress >= 1) && (iProgress <= 10) && (iProgress > g_iStep) )
   {  
       g_iStep++;
       var oSpan = document.getElementById( "sDot" + iProgress + "" );
       oSpan.className = 'FullDot';
   }
}

function OnCompletion()
{  
   document.location.replace('main.html');
}

function StartPreload()
{
   var szImages = new Array( "image1.gif", "image2.jpg", "image3.png", "etc..." );

   // Execute Image Preloader
   var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );
}

--></script>

</head>
<body onload="StartPreload()">

<center>
<table cellpadding="0" cellspacing="0" border="0" class="OuterBorder">
 <tr>
  <td>
     <span id="sDot1" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot2" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot3" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot4" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot5" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot6" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot7" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot8" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot9" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
     <span id="sDot10" class="EmptyDot">&nbsp;&nbsp;</span>
  </td>
 </tr>
</table>
</center>

</body>
</html>



Conclusion:

Well, I hope that you have learned something from this tutorial, and maybe even use such a preloader in your web sites!

Also note, that this Preloader won't work well with less than 10 images, but in that case you don't need a preloader like this one!

Please feel free to comment on this tutorial, if you noticed anything wrong or out of place in this tutorial, please don't hesitate to mention it!
I am interested in all feedback really, I'm curious about what you think of my writing, tutorial, methods used, code, etc.. Thanks!

I would have a working example to show, but my web host went down a while back. Sorry!

Read part 2 of this tutorial entitled "Extending the Image Preloader with PHP4"

[note=BuffaloHELP]Edited per request.[/note]

   Tue May 3, 2005    Reply         

Oh. My. Gosh. How long did it take you to write that?!

Back on topic. I saw this on a site before, but I was too lazy to check the source to see how they preloaded their images. Anyways, I'm going to try this out on the weekends when I have more time.

Good stuff man! Keep it up -_-

   Tue May 3, 2005    Reply         

Was it Mercury-Admin? I had hosting with them a while back, but they have gone down now, and I had originally posted the same tutorial there (that was the first time I posted it). Unfortunately for me, when their servers went down, I lost the original tutorial I posted, and I had to rewrite this one again from scratch.. Took about 2.5 hours today! -_-

Did you read it? What did you think?









   Tue May 3, 2005    Reply         


QUOTE (SystemWisdom)

Did you read it?  What did you think?

To be honest, no, I did not read it, but I will read it over the weekend or some other time when I have...time. Too much school work right now -_-

   Tue May 3, 2005    Reply         

I don't do codings well but to a novice user such as myself, would it not be easier to make a file called "preload.js" with the following code?

CODE

img1 = new Image();
img1.src = "images/nameofIMAGE1.jpg";

img2 = new Image();
img2.src = "images/nameofIMAGE2.jpg";

img3 = new Image();
img3.src = "images/nameofIMAGE3.jpg";

img4 = new Image();
img4.src = "images/nameofIMAGE4.jpg";

img5 = new Image();
img5.src = "images/nameofIMAGE5.jpg";

And just call "preload.js" from your <BODY> tag just like any other JavaScripts? This not only works with any amount of array, short or long, but I guess I see the disadvantage if you have growing number of images. But it's easier for me to edit on this script.

   Tue May 3, 2005    Reply         

Technically, that is exactly what my code is doing, but I wrapped it in a class so as to provide Events used for generating a Progress Bar for its display.
The code you posted will not give any visual indication of its progress, which was one of the problems solved by my tutorial.

It comes into play mainly when you have alot of images; with your suggested method the user will see nothing for a while until all images are loaded, whereas with my suggested method they will see a Dynamic Real-Time Progress Bar.

Either way, both methods work, and it boils down to opinion really.. Which method do you want to use? It is up to you! -_-

   Tue May 3, 2005    Reply         


One question: do you have a demo of this - a web page where you used all this, so we all can test it?

   Tue May 3, 2005    Reply         

I did, but my previous hosting (at Mercury-Admin.com) has gone down, so my site went with it... I have it backed up of course, and when I get new hosting I will put the site back online, which includes a working sample suited to my site, which loads approx. 60+ images.. (Although a portion of it is in PHP to dynamically load all the image URLs into the array)

The tutorial code is however, all Client-side Javascript + HTML really, so you could test it out rather quickly if you have say 10 or more images you could use to make a mock-up test environment..

As soon as I get new hosting and get my site back online, I will post a sample link to it.. Sorry for not having a Demo though, I know they are always nice!

Also, I will write another tutorial shortly, about the PHP portion I mentioned above..

   Tue May 3, 2005    Reply         

QUOTE (SystemWisdom)

Technically, that is exactly what my code is doing, but I wrapped it in a class so as to provide Events used for generating a Progress Bar for its display.
The code you posted will not give any visual indication of its progress, which was one of the problems solved by my tutorial.

It comes into play mainly when you have alot of images; with your suggested method the user will see nothing for a while until all images are loaded, whereas with my suggested method they will see a Dynamic Real-Time Progress Bar.

Either way, both methods work, and it boils down to opinion really.. Which method do you want to use? It is up to you!  -_-



AH! Well, since my site has one main index with two links I have my preload.js called in index.htm. And when the visitor decides to choose, let's say door 1 or door 2, the images are already preloaded before the visitation. I made it so for the simple factor of surfing pleasure. And since my graphics and images were small sized that I preloaded the entire image content even before any visitor views that page. It's like instant load. Therefore I personally would not need the status bar showing what images are being loaded. But now that you mention it my coding is very elementary and does not tell me what is going on. I am going to utilize your coding for my next project. Thank you for your post.

CODE


function PreloadImages()
{ if (document.images) { var imgFiles = PreloadImages.arguments; var preloadArray = new Array();
for (var i=0; i<imgFiles.length; i++) { preloadArray[i] = new Image; preloadArray[i].src = "icons/" + imgFiles[i] + ".gif"; } } }
PreloadImages(img#1, img#2, img#3, etc);


This was one of the codes I found on the web. Interesting condensation.

   Tue May 3, 2005    Reply         

very well put together tutorial, a bit long, but the longer the better, that way you can understand it all.
i am going to have to try this sometime soon, it looks quite useful. thanks

   Thu May 5, 2005    Reply         

I've been looking for a tutorial for an image preloader!

Thanks! :(

   Thu May 5, 2005    Reply         

Glad to hear you guys like it, thanks!

I finally have a web host again, Trap17! :(

So I have posted a working sample of this script on my site. The sample is just a rough copy sample, once I get my site integrated with Trap17 the sample will be a part of my actual site.

View the Sample Progress Bar Here!

Enjoy! :(

   Thu May 5, 2005    Reply         

QUOTE (SystemWisdom)

Glad to hear you guys like it, thanks!

View the Sample Progress Bar Here!


Hi,

Very nice tutorial; much appreciated. In particular I like the CSS progress bar.

One minor problem though. The sample link works just fine in Firefox and IE, but doesn' seem to be working properly in Safari (Mac). According to the browser status bar, it it is pre-loading the images, although no visual progress is shown in the progress-bar. After pre-loading is done, it just stays on the pre-loading page. The only way to advance is by clicking the "Skip Intro" link.

Any ideas?

Kai

   Sat May 28, 2005    Reply         

Thanks very much for this tutorial. I have seen others but thay have not worked as well or look as good as this script. I am astounded you are willing to provide this script for free, as I have seen others on the Internet costing upwards of $10. Thank you very much and good luck with any more coding you do! :rolleyes:

   Sun May 29, 2005    Reply         

QUOTE (Kai)

One minor problem though. The sample link works just fine in Firefox and IE, but doesn' seem to be working properly in Safari (Mac). According to the browser status bar, it it is pre-loading the images, although no visual progress is shown in the progress-bar. After pre-loading is done, it just stays on the pre-loading page. The only way to advance is by clicking the "Skip Intro" link.

Any ideas?


Thx for the input and I'm glad you liked it for the most part, but I have never had the opportunity to test or even view this script on a Mac. I am guessing Safari handles the DOM differently than other browsers... Now that I know there is a problem with Mac's Safari browser processing my script I will look into resolving the issue (and here I thought it worked perfectly! lol).

I am glad you pointed out the actual problems for me, which makes fixing it one step easier! I will reply as soon as I figure out a solution. Hopefully you can test it out again for me on your Mac Safari!! :lol:


QUOTE (rvalkass)

Thanks very much for this tutorial. I have seen others but thay have not worked as well or look as good as this script. I am astounded you are willing to provide this script for free, as I have seen others on the Internet costing upwards of $10. Thank you very much and good luck with any more coding you do! :rolleyes:


I couldn't imagine charging people money for my tutorials (they probably wouldn't get read!), I mainly write them so that I can add them to my Resume/portfolio, maybe they will help land me a job!

I'm especially glad to see such positive feedback from this tutorial (makes me want to write more)!

   Sun May 29, 2005    Reply         

QUOTE (SystemWisdom)


I am glad you pointed out the actual problems for me, which makes fixing it one step easier!  I will reply as soon as I figure out a solution.  Hopefully you can test it out again for me on your Mac Safari!! :D



Just let me know if you would like me to do any further testing, or provide additional info if needed to make your job easier to find a fix.

~ Kai

   Thu Jun 9, 2005    Reply         

I haven't figured it out yet :(

Mac confuses me :P

Do you know any Javascript at all? It sounds like a few javascript functions are not being recognized by Safari, amd I am having alot of trouble finding out which ones.. Theoretically, it should work fine, unless Safari browsers haven't followed basic standards for DHTML.. :(

Maybe it has to do with it being on a Mac? Did you test it with IE and Firefox from Mac too, or was that on Windows?

I am almost at the point of resolving to detect the Safari browser on Mac and just skip preloading altogether, so it doesn't cause problems... :(

Well, ima keep looking for an answer, wish me luck! :P

   Sun Jun 12, 2005    Reply         

Well i will check this out :( ...processing on the client side free's up the server and this script can come quite handy on slower connections :P ...

Thanks m8, if it works wrong i will came back to you for help :P

Best Regards

Nuno

   Thu Jun 16, 2005    Reply         

Hi

In fact, the problem is that Safari seems to not support self-reference in javascript class.
The two last variables in ImagePreload.prototype.OnLoad are marked as "undefined".

For those who wants to debug in Safari with MacOS, type "defaults write com.apple.Safari IncludeDebugMenu 1" on a terminal and then restart Safari. A "debug" menu will be present on the top of the screen.

I think it's a safari bug, but it should exists a workaround.

Regards,
Riton

QUOTE (SystemWisdom)

I haven't figured it out yet :)

Mac confuses me :)

Do you know any Javascript at all? It sounds like a few javascript functions are not being recognized by Safari, amd I am having alot of trouble finding out which ones.. Theoretically, it should work fine, unless Safari browsers haven't followed basic standards for DHTML.. :(

Maybe it has to do with it being on a Mac? Did you test it with IE and Firefox from Mac too, or was that on Windows?

I am almost at the point of resolving to detect the Safari browser on Mac and just skip preloading altogether, so it doesn't cause problems... :(

Well, ima keep looking for an answer, wish me luck! :)
Link: view Post: 150476

   Tue May 15, 2007    Reply         

Oh, this is a very interesting code, yet really long :) I'll try to give it a look patiently this weekend and see if I can follow all the steps.

Thank you for sharing it.

   Wed May 16, 2007    Reply         

wow great tutorial,

I needed this functionality a long time before, but never tried to do it. Thanks for sharing this tutorial.

I'd prefer preloading images using CSS rather than Javascript, just for the crooss browser compatability.

   Wed May 16, 2007    Reply         

Greetings wow m8 thats a nice tutorial man

thanx
this can sure help me a lot whit some things..

i like to see more of your tutorials..


Thanx SystemWisdom

   Wed May 16, 2007    Reply         

Great and lengthy tutorial there! I managed to read most of it and found it easy to understand and useful for implementing on my own site. Though a condensation of the tutorial or the codes are welcomed. By the way, how would you do it if i want a visitor to view a video or a advertisement of my site while waiting for the site to finish loading?

   Thu Sep 20, 2007    Reply         

It only works in Internet Explorer?
Image Preloader With Progress Bar Status

I used this script for my website, but it only works in the internet explorer... In any other browser it won't work - what could be the problem? I'm using "firebug" for firefox and there are no errors - but only in the ie it comes to the "OnCompletion()"-function.

But the script & tutorial is very well work!! congratulations!

-reply by daGizmo

   Mon Sep 1, 2008    Reply         

Rob (systemwisdom)

This is a great tutorial, I'm not a senior developer or anything like that, yet I was able to read the code and understand your instructions. Your code is great, works great and was easy to implement in my own website.

It is highly appreciated that you wanted to provide the script for free to the community and as soon as my coding skills improve, I'll make sure I give something back to the web development community.

I'm a freelance designer/developer just trying to make a name for myself and I appreciate all the help I can get; again many thanks.

Said that, and after I read the replies from everybody, I implemented the code into my own site and it works like a charm.

I tested it with IE7, Mozilla Firefox 2 and 3 (windows and Mac), Safari for (windows and Mac), Opera and even Chrome. It worked just fine in every browser, obviously I know better than most users so I had Javascript enable and made sure I cleared the cache every time I tested it.

I wanted to give my website a professional look, although it's just me doing all the work and this script helped me accomplish a better user experience.

If anybody wants to check it out, the URL is "www.fatlizardmedia.com"

I made a couple changes to the CSS, just to match my color scheme, but other than that it was great to find this script and I'll make sure that I " Render unto Caesar what is Caesar's".

Thanks again.
Juan C Rois

Just a note: The images in my website are PNG 24 with Alpha transparency and IE6 does not render them appropriately, still working on that CSS or Javascript hack.

   Tue Jan 6, 2009    Reply         

I Haven't tested your code yet, but it is what I need I guess, cause I wanna do a small game attempt, and I would need to use a image lots of times on the same frame instantly, with this sort of pre load I have the possibility right?

Anyway, I can host your image load demo if you wish, just send me a zip of everything.

   Mon Feb 23, 2009    Reply         

Plain awesome! I really needed this! I'm used to flash and I'm doing preloaders there but I wanted to take a break from flash and do some other things. So I started learning JavaScript. It's really helpful for any of us but I think It's great for the ones who own a freestorage files website. Also well explained with many explanations and hints. Keep the good job.
Cheers!

   Tue Mar 3, 2009    Reply         

QUOTE (SystemWisdom)

Tutorial: Image Preloader with Progress Bar, by Rob J. Secord, B.Sc. (SystemWisdom)


Description:
A Tutorial for a Client-Side Image Preloader with Dynamic Real-Time Progress Bar Indicator written in JavaScript!
Tested to work with 4 Major Internet Browsers: Firefox, MSIE, Netscape, Opera

(Complete sample solution provided at end of tutorial, just put it on your web-server, add your images and go!)


Intended Audience:
Beginner to Intermediate Web Developers.

Although this tutorial will cover some advanced aspects of JavaScript, I will try to explain it all as thoroughly as possible. At the same time, I will keep the functionality basic and leave room for you to expand on it as you see fit.

Assumed knowledge:
-- OOP Concepts such as Classes
-- Arrays, Loops
-- JavaScript Basics
-- Working with *.js files


Background:
Many websites rely on Images for the Graphical User Interface (GUI) of their Web Applications. This is great for visual appeal, but when the site consists of mostly images (both large and small) to make up the entire layout, then your visitors download time ends up taking longer. And as all of us should already know, no one likes to wait too long for a site to load without at least a visual indication of its progress.
On average, people with broadband connections will wait up to 10 seconds for a site to load -- which isn't very much time at all!

Another problem while the site is loading for the visitor, is that they see the site slowly take shape, as single images are downloaded individually and fitted into the screen so as to form the layout. It would be much more appealing if the site were to load all at once, images and text loaded and in place all ready to go!

The solution? An Image Preloader with Progress Bar Status!

Great! But how?


Theory:
Utilizing the Document Object Model (DOM) of HTML with the power of JavaScript, we can access all of our sites images as Objects, giving us access to the Events fired by the Image Objects.

On top of that, when we load an Image in JavaScript via the DOM, we point to the location of the Image via a Uniform Resource Locator (URL) just as we would with regular HTML <IMG> tags. This means that the Image gets loaded into the memory of the visitors computer. Now, when we call for an image in our main page like <img src="img/blah.gif">, the relative Image URL is already loaded, and thus the Image gets displayed immediately.

Now as I mentioned before, when we load an Image via JavaScript and the DOM, we have access to the Events of that Image Object, and to name a few of those events we have:

OnLoad()
OnError()
OnAbort()

Pretty straight-forward really; when the Image is completely loaded the OnLoad() event gets called. If an error occurs while loading the Image the OnError() event gets called. And lastly, if the user (visitor) leaves the page (or closes browser) the OnAbort() event gets called.

In this tutorial we will only need to deal with the OnLoad() event, however, the other events could be of great use, and should be simple enough for you to implement on your own after reading this tutorial. (I will leave a basic code structure for the extra events in the sample at the end of this tutorial, but I will not be explaining them in great detail. That could be left open for discussion!)

The main purpose of implementing the OnLoad() event is to keep track of our loaded images so we can display a Real-Time Progress Bar to our users. The idea here is to keep a counter of how many images have been loaded, so we know our progress. (Hint: You could also track how many Images fail to load via the OnError() event!)

Finally, we have to display that progress on the screen, which can be solved with the use of Span Tags and CSS! In this tutorial I will simply lay out 10 boxes (via Span Tags) each with a Grey BackGround Style, and as the progress increases, I will change the style of each consecutive box to a Blue BackGround, giving a visual appearance of a Progress Bar!

I hope you were able to follow along thus far, we are going to tackle the actual code next! biggrin.gif


Implementation:
All right! We are going to start on the last section of this tutorial where we actually get to build something! (If you feel daunted by the length of this tutorial at this point, know that the longest section lies yet ahead! Fear not, brave reader!)

Now, I am going to take you through 3 stages in building our Image Preloader with Progress Bar:

1) Writing the Preloader class in JavaScript -- This is the core of our preloader, and where most of the theory from above takes place;
2) Writing the Progress Bar Page -- This is a simple HTML page used soley to display the Progress Bar to our visitors;
3) Putting it all together -- This is where we will implement the Preloader Class into our Progress Bar Page to make it all work!

Due to the 'intertwined' nature of the code, it will be difficult to explain, and you may benefit from reading parts of this section more than once after having read it all. Try to remember any parts that confuse you, and come back to it later, after reading more of the tutorial.



Writing the Preloader class in JavaScript:
We start off by making a new file called 'ipreload.js'. This will be our Image Preloader Class File which will contain a single class declaration. If you have never worked with Classes in JavaScript before, they may seem odd at first. This is due to the fact that JavaScript (ECMAScript) lacks full support for user-defined class types. sad.gif But fret not, my friend! I shall explain them anyway! biggrin.gif

(Note: If you already understand classes in JS, you may safely skip the following quoted section)


Okay, moving on.. Now that we understand classes in JavaScript, we will begin with our 'ImagePreload' Class. I will first show you the completed class and then I will explain how it works, this way you will have reference to what I am writing about.

File = ipreload.js:

CODE

<!--

function ImagePreload( p_aImages, p_pfnPercent, p_pfnFinished )
{
// Call-back functions
this.m_pfnPercent = p_pfnPercent;
this.m_pfnFinished = p_pfnFinished;

// Class Member Vars
this.m_nLoaded = 0;
this.m_nProcessed = 0;
this.m_aImages = new Array;
this.m_nICount = p_aImages.length;

// Preload Array of Images
for( var i = 0; i < p_aImages.length; i++ )
this.Preload( p_aImages[i] );
}

ImagePreload.prototype.Preload = function( p_oImage )
{
var oImage = new Image;
this.m_aImages.push( oImage );

oImage.onload = ImagePreload.prototype.OnLoad;

oImage.oImagePreload = this; // Give the Image Object a Reference to our Class..
oImage.bLoaded = false; // Custom value added to track state
oImage.source = p_oImage; // Original Source Path to Image (for later use)
oImage.src = p_oImage; // Image Object Source Path
}

ImagePreload.prototype.OnComplete = function()
{
this.m_nProcessed++;
if ( this.m_nProcessed == this.m_nICount )
this.m_pfnFinished();
else
this.m_pfnPercent( Math.round( (this.m_nProcessed / this.m_nICount) * 10 ) );
}

ImagePreload.prototype.OnLoad = function()
{
// 'this' pointer points to oImage Object because this function was previously assigned
// as an event-handler for the oImage Object.
this.bLoaded = true;
this.oImagePreload.m_nLoaded++; // Access our Class with the Reference assigned previously..
this.oImagePreload.OnComplete();
}

//-->


Okay, we have a class called 'ImagePreload' whose constuctor takes 3 parameters, namely p_aImages, p_pfnPercent and p_pfnFinished respectively.
The first parameter 'p_aImages' is an Array of Strings which holds the URL of all of the images we want to Preload. We will build this array outside of this class later on, and pass it into the class when we instantiate an object of the class.
The second and third parameters are actually Functions (or more precisely, Function Pointers) that we want our class to have access to without being part of the class itself. We will deal more with these later.

Now looking into the Constructor function for our class we will see that the last two parameters are being assigned to Member Variables of the class, so that they are stored locally and are available for the class to call as functions later on. They are commented as "Call-Back functions", and that is exactly what they are: They "Call-Back" to the code that "Called" our Class' Constructor/Member Functions. More over, they can be considered Events that are raised by our class. I will elaborate on this further later in this tutorial. Moving on..

Next you will see some more Member Variables being declared within the class, namely: m_nLoaded, m_nProcessed, m_aImages and m_nICount. I will try to explain them each briefly, yet adequately each in a single paragraph of their own;

Since our class will keep a counter for all of the Images it loads, we will need a Member Variable to hold that counter, starting at 0. The 'm_nLoaded' Member Variable serves this purpose. After each successfully loaded image, 'm_nLoaded' will be incremented by 1.

Our class will also keep a counter for the number of Processed Images (meaning the images that our Progress Bar has accounted for) to calculate the Overall Progress. The 'm_nProcessed' Member Variable serves this purpose. This should be unaffected by Image Load Failures from the OnError() events (Be sure to note that if you plan to add error handling to this class).

Our class will also be storing the Image Objects in Memory on the visitors machine, so that they are considered "Preloaded", and we will do this by creating an Array, and adding the Image Objects to the Array as the images are loaded. The 'm_aImages' Member Variable serves this purpose. Note that this Member variable starts with 'm_' unlike the parameter with the same name, which starts with 'p_'.

Finally, our class will need to know the total number of images it is preloading in order to compute a progress, and yes, the 'm_nICount' Member Variable serves this purpose! We can get this value from the Arrays 'length' property, which returns the number of elements in the array.

Okay, so that should explain most of the Constructor function to you, and now we will look at the last part of the code in the constructor function.. Hopefully, you've kept up with me and the reference code above, if not here is the part I am refering to next:

CODE

// Preload Array of Images
for( var i = 0; i < p_aImages.length; i++ )
this.Preload( p_aImages[i] );


This is a basic FOR loop which we will use to walk through the Array of Image URLs that were passed into the function as the First Parameter. We will then pass each individual URL string to our class' Preload() Member Function which handles the actual "Preloading" of each Image Object.
As you can see by now, as soon as you Instantiate the ImagePreload Object passing it the Array of Image URLs, the preload process will start immediately, and within seconds (hopefully) all of your Images will be preloaded!

But wait! What about the progress bar?!
Since the ImagePreload class will be taking care of loading all of those images, how will the Progress Bar know what's going on?

This is solved by those two "Call-Back" functions I mentioned earlier. Since the Progress Bar itself will be completely seperate code with a seperate responsibility, it will have two functions of its own. One function will update the Progress Bar Display, and the other function will redirect to the main page of your site when the preloading is done!

Now, as I mentioned earlier, the two "Call-Back" functions were assigned to Member Variables which means that any function in our class can now call either of those two functions whenever it needs to. Basically, we will use them as Events, so that everytime an image is loaded, the progress is calculated and the Event (Call-Back Function) is called, sending back the current progress! Now since those functions actually belong to (and are coded in) the Progress Bar, then they will get called and Instantly Update the Progress or Redirect the Page Accordingly!

Hopefully that made sense to you, either way, I shall explain it in more detail.
In reference to the code above, we should now be looking at the Preload() function declared as:

CODE

ImagePreload.prototype.Preload = function( p_oImage )


Recall that this function is executed in the FOR loop in the Constructor of our Class, and is being passed a URL string of the Image to Preload. Looking into the function we notice the first two statements, the first line just instantiates an Image Object (which is part of JavaScript) and the second line adds that Image Object into our Array of Image Objects which we had declared earlier in the Constructor. Memory space is now reserved for this image on the visitors computer!

Next, we see a see a statement dealing with the OnLoad() event of the Image object:

CODE

oImage.onload = ImagePreload.prototype.OnLoad;


We are basically telling the Image Object to Call a Member Function in our class when the Image is loaded, and our Member Function will be responsible for tracking the progress.
But there is one tricky thing to note about this assignment; the 'ImagePreload.prototype.OnLoad' function now "belongs" to the Image Object, even though the function is implemented in our class. The important point is that the 'this' keyword within our 'ImagePreload.prototype.OnLoad' Member Function will not point to our 'ImagePreload' Object Instance, but rather the Image Object Instance. (Crude Analogy: this is not this anymore, it is that! tongue.gif)
This may seem very confusing at first, but don't give up! It makes sense! Besides, you don't have to understand it, just know that it works and how to use it!

Now if you didn't understand what I just wrote, then the next statement won't make much sense to you:

CODE

oImage.oImagePreload = this; // Give the Image Object a Reference to our Class..


Basically, we want the Image Object to have access to our Class. This is used to access the Call-Back functions in our class from within the OnLoad() function which now belongs to the Image Object. Make sense? I hope so, I don't know how else to explain it! sad.gif But don't fret my friend, read further! Maybe it will clear up for you later!)

The next few statements are much simpler:

CODE

oImage.bLoaded = false; // Custom value added to track state
oImage.source = p_oImage; // Original Source Path to Image (for later use)
oImage.src = p_oImage; // Image Object Source Path


The first is just a simple boolean flag to indicate whether the Image Object has been loaded or not, which is set to False by default.
The next two lines are pretty much the same, except that the first one is a custom variable and the second one is actually an Image Object Property 'src', much like in HTML <IMG> Tags. When you assign a a URL to this property it will be interpreted immediately and the actual image will be loaded into the Memory space we had reserved earlier! This is the load process internally, and when then image is done loading the OnLoad() event gets fired calling our OnLoad() Member Function.
The custom variable 'source' is used for referencing the original path & filename of the image (if you should need to) whenever you need to.

And that is the Preload() function!! Hopefully, you now understand how the actual Preload process works!

We will now move on to the final 2 Member Functions: OnLoad and OnComplete..

CODE

ImagePreload.prototype.OnLoad = function()
{
// 'this' pointer points to oImage Object because this function was previously assigned
// as an event-handler for the oImage Object.
this.bLoaded = true;
this.oImagePreload.m_nLoaded++; // Access our Class with the Reference assigned previously..
this.oImagePreload.OnComplete();
}


Now, since the OnLoad() function belongs to the Image Object, we can directly manipulate the variables we gave it earlier, namely the 'bLoaded' boolean flag. We will set this to true, to indicate that this image object was loaded successfully.
We also need to increment the counter holding the total number of Loaded Images thus far, which is a Member Variable that belongs to the ImagePreload class, and this function doesn't! So, we access the reference we stored earlier as a custom variable in the Image Object. We can now use that reference object to access anything in our ImagePreload class, which the next two statements do; the first of which simple increases the counter, and the second calls our final Member Function 'OnComplete'.

CODE

ImagePreload.prototype.OnComplete = function()
{
this.m_nProcessed++;
if ( this.m_nProcessed == this.m_nICount )
this.m_pfnFinished();
else
this.m_pfnPercent( Math.round( (this.m_nProcessed / this.m_nICount) * 10 ) );
}


This function is pretty simple, it belongs to our ImagePreload class and simply increments the counter of Processed Images. (Note: If the # of Processed Images is not equal to the # of loaded images, then some of your imges fail to load. But the progress will still compute correctly!)

Next it checks to see if it has processed all the required images, and if so it raises the "Finished" event, which tells the Progress Bar code to redirect to the main page. However, if it is not done loading all of the required images, it will raise the "Percent" event, which tells the Progress Bar to update the display according to the Percent Complete!
(The Percentage is rounded to a single digit number from 1-10, but this could easily be changed to suit your needs).

Whew! Take a break! Your done Stage 1 of the Implementation Section! It's pretty much downhill from here! biggrin.gif


Writing the Progress Bar Page:
I am going to keep this part of the tutorial a simple as possible, since it only deals with basic HTML tags, and some simple CSS styles for appearance. This is going to be the GUI of the Progress Bar, and most of you will probably change the appearance of it anyway.

Basically, you just need an empty webpage (preferably no images, otherwise, what's the point of the Preloader?)
So it could look something like this (keeping it simple):

File = index.html:

CODE

<html>
<head>
<style type="text/css"><!--

.OuterBorder
{ border:1px solid #426394;
padding: 2px 5px 2px 5px;
}
.FullDot
{ border:1px solid #426394;
background-color:#DAE1EB;
cursor:default;
}
.EmptyDot
{ border:1px solid #426394;
background-color:#F3F6FA;
cursor:default;
}
//--></style>
</head>
<body>

<center>
<table cellpadding="0" cellspacing="0" border="0" class="OuterBorder">
<tr>
<td>
<span id="sDot1" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot2" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot3" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot4" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot5" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot6" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot7" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot8" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot9" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot10" class="EmptyDot">&nbsp;&nbsp;</span>
</td>
</tr>
</table>
</center>

</body>
</html>


You'll notice it is just 10 span tags within a bordered table, each span tag representing an 'EmptyDot'. As the progress increases, the 'EmptyDots' will become 'FullDots'.

Well, nothing special there, just some regular HTML/CSS stuff.. So, we move on, to the Final Stage!


Putting it all together:
This stage shouldn't be too tricky either.. First we will have to include or ImagePreload class file into our HTML page, then we will write our 2 functions that will respond to the events raised by our ImagePreload class, as well as a third function to start the whole Loading process rolling! Also, we will add an extra link on the page, that will give the visitor the option to "Skip Preload" and go straight to the main page..

First, including the Preloader, which most of you I assume will be familiar with. Simple JS file including:

CODE

<script type="text/javascript" language="JavaScript" src="ipreload.js"></script>


Now, we want to create 3 more JavaScript functions inside the <HEAD> tag of the web page, as mentioned earlier. I will first show you the completed HTML file, and then like before, I will explain it all to you, with the code as reference:

File = index.html:

CODE

<html>
<head>
<style type="text/css"><!--

.OuterBorder
{ border:1px solid #426394;
padding: 2px 5px 2px 5px;
}
.FullDot
{ border:1px solid #426394;
background-color:#DAE1EB;
cursor:default;
}
.EmptyDot
{ border:1px solid #426394;
background-color:#F3F6FA;
cursor:default;
}
//--></style>

<script type="text/javascript" language="JavaScript" src="ipreload.js"></script>

<script type="text/javascript" language="JavaScript"><!--

var g_iStep = 0;

function OnImgUpdate( iProgress )
{
if( (iProgress >= 1) && (iProgress <= 10) && (iProgress > g_iStep) )
{
g_iStep++;
var oSpan = document.getElementById( "sDot" + iProgress + "" );
oSpan.className = 'FullDot';
}
}

function OnCompletion()
{
document.location.replace('main.html');
}

function StartPreload()
{
var szImages = new Array( "image1.gif", "image2.jpg", "image3.png", "etc..." );

// Execute Image Preloader
var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );
}

--></script>

</head>
<body onload="StartPreload()">

<center>
<table cellpadding="0" cellspacing="0" border="0" class="OuterBorder">
<tr>
<td>
<span id="sDot1" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot2" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot3" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot4" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot5" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot6" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot7" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot8" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot9" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot10" class="EmptyDot">&nbsp;&nbsp;</span>
</td>
</tr>
</table>
</center>

</body>
</html>


Now, the first thing you'll notice, is that there is not much left to do! Yay! I'm exhausted from typing! Okay, going back on track now...

You could seperate the CSS and put it in an external *.css file if you prefer, but I kept this tutorial simple, with the use of only 2 files (plus the rest of your website).

The first thing to note in the newely added JavaScript code block is:

CODE

var g_iStep = 0;


This is a global variable that needs to maintain its value, hence it is declared outside of any specific code block. The reason for this variable is simple; The Progress Bar display in this tutorial only has 10 steps, but you could have 50+/- images. Since the Update Event gets called for every loaded image, then it is unnesseccary to Update the progress bar with every image, instead we track when the Percent value changes, store the new value in the 'g_iStep' variable and Update the display only if the 'g_iStep' variable has increased!

This all takes place in the 'OnImgUpdate' function:

CODE

function OnImgUpdate( iProgress )
{
if( (iProgress >= 1) && (iProgress <= 10) && (iProgress > g_iStep) )
{
g_iStep++;
var oSpan = document.getElementById( "sDot" + iProgress + "" );
oSpan.className = 'FullDot';
}
}


Should be pretty straight-forward from what I wrote above. Worth noting though, is the 'oSpan' variable, which refers to one of the HTML <SPAN> tags in the DOM that make up the Progress Bar Display. We can get access to this Span Tag as an Object via the 'document.getElementById()' function which is part of JavaScript. All we need to do, is tell the function the ID of the Span Tag, which we have named successively ranging from "sDot1" to "sDot10". Each ID corresponds to a Progress Interval. Now that we have access to the Span Tag Object, we can change its Style via the 'ClassName' property, which we then change to 'FullDot' so as to indicate a Full Dot, and Progress on the Progress Bar!

Well, that takes care of the Progress Bar actually moving! Now we have to Redirect to the Main page of your site once it is complete. Our ImagePreload class will take care of raising the Event when all of the images are loaded, we just need to do something now when that event occurs.

This will happen in our 'OnCompletion()' function:

CODE

function OnCompletion()
{
document.location.replace('main.html');
}


Pretty simple really, if you understand the Basics of Javascript. We just Redirect to the Main page of your site (assuming the page is called 'main.html', if not point it to your page). Also, the replace() function has the added benefit of removing the preloader from the history, so people don't click there Back button and go to a Preloader page!


And finally, where my tutorial draws near to its conclusion, we put it all together in one Simple function:

CODE

function StartPreload()
{
var szImages = new Array( "image1.gif", "image2.jpg", "image3.png", "etc..." );

// Execute Image Preloader
var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );
}


Now the first thing you will notice, is the Array of Image URLs, these are "Relative" URLs and should point to the actual image files on your webserver that you wish to preload. This script will not work "As Is" unless you point to Your image files in the Array.

How ever you build that Array of images is up to you, I personally use PHP to recurse through my /img/ directory making a list of all the Image path/filenames it finds, and then outputs that list into the Preloader Pages Image URL Array above. That would be a tutorial of it's own, so I will save going into detail about that for another day!

Next, is the MAin line of execution for our Preloader script.

CODE

var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );


In this one line is where we Instantiate a new 'ImagePreload' Object, passing to its Constructor 3 parameters: the Array of Image URLs, the 'Update' Event Function Pointer and the 'Finished' Event Function Pointer which starts the whole Preloading process in motion!

The 'StartPreload' function should be called as soon as the Preload Page loads, so we will call it in the onload event handler of the Body Tag:

CODE

<body onload="StartPreload()">



And there you have it! An Image Preloader with Progress Bar Status in JavaScript!

So, as the End-Result, we have the 2 completed Files (with the extra stuff I mentioned left in):

File = ipreload.js:

CODE

<!--

function ImagePreload( p_aImages, p_pfnPercent, p_pfnFinished )
{ // Call-back routines
this.m_pfnPercent = p_pfnPercent;
this.m_pfnFinished = p_pfnFinished;

// Class Member Vars
this.m_nLoaded = 0;
this.m_nProcessed = 0;
this.m_aImages = new Array;
this.m_nICount = p_aImages.length;

// Preload Array of Images
for( var i = 0; i < p_aImages.length; i++ )
this.Preload( p_aImages[i] );
}

ImagePreload.prototype.Preload = function( p_oImage )
{ var oImage = new Image;
this.m_aImages.push( oImage );

oImage.onload = ImagePreload.prototype.OnLoad;
oImage.onerror = ImagePreload.prototype.OnError;
oImage.onabort = ImagePreload.prototype.OnAbort;

oImage.oImagePreload = this;
oImage.bLoaded = false;
oImage.source = p_oImage;
oImage.src = p_oImage;
}

ImagePreload.prototype.OnComplete = function()
{ this.m_nProcessed++;
if ( this.m_nProcessed == this.m_nICount )
this.m_pfnFinished();
else
this.m_pfnPercent( Math.round( (this.m_nProcessed / this.m_nICount) * 10 ) );
}

ImagePreload.prototype.OnLoad = function()
{ // 'this' pointer points to oImage Object
this.bLoaded = true;
this.oImagePreload.m_nLoaded++;
this.oImagePreload.OnComplete();
}

ImagePreload.prototype.OnError = function()
{ // 'this' pointer points to oImage Object
this.bError = true;
this.oImagePreload.OnComplete();
}

ImagePreload.prototype.OnAbort = function()
{ // 'this' pointer points to oImage Object
this.bAbort = true;
this.oImagePreload.OnComplete();
}

//-->



File = index.html:

CODE

<html>
<head>
<style type="text/css"><!--

.OuterBorder
{ border:1px solid #426394;
padding: 2px 5px 2px 5px;
}
.FullDot
{ border:1px solid #426394;
background-color:#DAE1EB;
cursor:default;
}
.EmptyDot
{ border:1px solid #426394;
background-color:#F3F6FA;
cursor:default;
}
//--></style>

<script type="text/javascript" language="JavaScript" src="ipreload.js"></script>

<script type="text/javascript" language="JavaScript"><!--

var g_iStep = 0;

function OnImgUpdate( iProgress )
{
if( (iProgress >= 1) && (iProgress <= 10) && (iProgress > g_iStep) )
{
g_iStep++;
var oSpan = document.getElementById( "sDot" + iProgress + "" );
oSpan.className = 'FullDot';
}
}

function OnCompletion()
{
document.location.replace('main.html');
}

function StartPreload()
{
var szImages = new Array( "image1.gif", "image2.jpg", "image3.png", "etc..." );

// Execute Image Preloader
var oPreload = new ImagePreload( szImages, OnImgUpdate, OnCompletion );
}

--></script>

</head>
<body onload="StartPreload()">

<center>
<table cellpadding="0" cellspacing="0" border="0" class="OuterBorder">
<tr>
<td>
<span id="sDot1" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot2" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot3" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot4" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot5" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot6" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot7" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot8" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot9" class="EmptyDot">&nbsp;&nbsp;</span>&nbsp;
<span id="sDot10" class="EmptyDot">&nbsp;&nbsp;</span>
</td>
</tr>
</table>
</center>

</body>
</html>



Conclusion:

Well, I hope that you have learned something from this tutorial, and maybe even use such a preloader in your web sites!

Also note, that this Preloader won't work well with less than 10 images, but in that case you don't need a preloader like this one!

Please feel free to comment on this tutorial, if you noticed anything wrong or out of place in this tutorial, please don't hesitate to mention it!
I am interested in all feedback really, I'm curious about what you think of my writing, tutorial, methods used, code, etc.. Thanks!

I would have a working example to show, but my web host went down a while back. Sorry!

Read part 2 of this tutorial entitled "Extending the Image Preloader with PHP4"

Notice from BuffaloHELP:
Edited per request.

Link: view Post: 137857

a very long tutorial...
i wonder how much time you need to write this tutorial...?
anyways, great tutorial! you explained everything very clear
i'm sure this will be very easy for people to understand it... :D

   Wed Mar 11, 2009    Reply         

How to use that?Image Preloader With Progress Bar Status

Hi,

sorry friends, I read the above cursory, and I'm Astigmatic you know [img]/txtmngr/images/smileys/smiley1.Gif[/img]

Could someone tell me, How we can use that?!

I mean, after preloading images, when we are here:

<img name='something' src= ###   >

what should we write stead of ### ?

-reply by Armin

   Mon Aug 3, 2009    Reply         

I have a question.....
Will this work if I am calling the images with an XML file?

:P

   Fri Dec 18, 2009    Reply         

This is a good concept, although it's not implemented well in this tutorial.

Since you're relying on javascript anyway, get rid of the preloader page and just have the preloader bar appear over the current page with the content blacked out below it. This eliminates an extra page transfer (GET request to the server, that is, thus reducing bandwidth). It also eliminates the possibility that someone could link to the page following being transferred away from the preloader, meaning all other visitors who view the page through the link skip preloading altogether.

The method I proposed is also much simpler and more concise, as it eliminates the need for you to have to manually enter each image's location information onto another page. Additionally, it'd be reusable across all pages without any changes, whereas the above script is only good for one page (set of images), unless it's specifically modified for another.

So, how would you go about doing it this way? I'm not going to actually create the thing for you, but assuming you have a good grasp of Javascript, just do the following:

1. Black out the page by creating a div with a high z-index value and positioning it absolutely over all other content.
2. Use the getElementByTagName method to fetch all <img> elements on the page.
3. Build a throw-away array containing each unique image src and attach an onload event listener to each. Use the onload event listener to trigger a preloader update method.
4. When the preloader update method is called (when another image on the page has been loaded), increase a variable count of images that have been preloaded and adjust the preloader bar to reflect the progress.
5. When the final preloader update has been called (when the preload count reaches the throw away array length, that is), set the array to null and remove the preloader bar and screen fader divs to reveal the page below (with all images loaded).

Problem solved. No need to worry about having an extra page/redirect, source duplication, or direct linking, and you get the added benefit of a JS file that's reusable on any page.

   Fri Dec 18, 2009    Reply         

Quickly Post to Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers! w/o signup Share Info about Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers! using Facebook, Twitter etc. email your friend about Image Preloader With Progress Bar Status Pure Client-Side JavaScript tested in 4 Browsers! Print
Reply / Comment New Discussion / Topic Share / Bookmark E-Mail a Friend Print

Similar Topics:

Download Statusbar Extension For Fi...

I was just wondering if anyone was sharing the same problem. And yes, I intend to send some correspondence to see if I can get an answer as to why this happens. After installing the Download Statusbar extension for Firefox thanks to SM's Look-Like-Chrome-with-Firefox thread, I immed ...more

   25-Sep-2008    Reply         

How To Customize Ff Status toolbars...

I'm using FF 3.0.11 and I'm wondering is there any way to customize the status and toolbars a bit more? I know you can right click on the tool bars to move buttons around but is there any way extension or built in way to move entire toolbar easily? It's just an appearance thing, but it b ...more

   24-Jun-2009    Reply         

Show A Progress Bar In Vista And Xp

How can I call up a progress bar in vista and xp? One where I set the properties myself. And without installing stuff. I found one for batch, but i want something else. ...more

   19-Aug-2010    Reply         

Saint-michael Html Tips And Tricks Issue #3 exploring meta tags and what uses they have   Saint-michael Html Tips And Tricks Issue #3 exploring meta tags and what uses they have (0) (0) Saint Michael's Dhtml Tips And Tricks Issue #2 well i thought i catch up in my issues so heres some more  Saint Michael's Dhtml Tips And Tricks Issue #2 well i thought i catch up in my issues so heres some more