How to Create an Awesome Image Preloader

How to Create an Awesome Image Preloader

Tutorial Details
  • Topic: jQuery, JavaScript
  • Difficulty: Intermediate
  • Estimated Completion Time: 30 Minutes
  • Flickr Images Courtesy of: noe0712

How often do you find that images in a website load gracefully; the kind where a loading icon first appears, and the image then fades in, once loaded? This technique can greatly boost the performance of your website. If you’re not already familiar with this method, you’re in luck! Today, we’ll create a preloader plugin for your projects. Intrigued? Let’s get started!


Step 1: Setting Up Your Workspace

First, we are going to setup the project folder for this tutorial. We’ll need:

  • Our main HTML file
  • CSS folder for our stylesheet and loading icon ( in ‘i’ folder)
  • JS folder for jQuery and our plugin
  • IMAGES

Step 2: The HTML

We’re going to start off with the HTML code.

<DOCTYPE html>
<html>
<head>
<meta charset=utf-8" />
<title>Image Preloader</title>

<script type="text/javascript" src="js/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="js/jquery.preloader.js"></script>

<link rel="stylesheet" href="css/preloader.css" type="text/css" />

</head>

<body>

<div id="container">
    <h2>Preloader - Load images with style</h2>
      
    <ul id="gallery" class="clearfix">
        <li><p><a href="#"><img src="images/1.jpg" /></a></p></li>
        <li><p><a href="#"><img src="images/2.jpg" /></a></p> </li>
        <li><p><a href="#"><img src="images/3.jpg" /></a></p> </li>
        <li><p><a href="#"><img src="images/4.jpg" /></a></p></li>
        <li><p><a href="#"><img src="images/5.jpg" /></a></p> </li>
        <li><p><a href="#"><img src="images/6.jpg" /></a></p> </li>
        <li><p><a href="#"><img src="images/7.jpg" /></a></p> </li>
        <li><p><a href="#"><img src="images/8.jpg" /></a></p> </li>
        <li><p><a href="#"><img src="images/9.jpg" /></a></p> </li>
    </ul>

</div>

Nothing fancy here: just plain HTML code for a simple gallery. We have imported jQuery, our plugin jquery.preloader.js (currently blank), and our preloader’s stylesheet. To finish up, we’ll add an unordered list, which will contain list items as images wrapped by an anchor tag (usually done in a website for opening a lightbox or linking to a site).

Note that the extra p tag wrapping each anchor is used for the purpose of styling the image; they are not explicitly required.


Step 3: The CSS

Now, we’re going to create a preloader.css stylesheet in the css folder, and, inside that, create a subfolder i in which we will keep our preloader icon. Preloaders.net have a nice collection of loading icons that you can choose from. Add the following code to your stylesheet:

* { margin:0; padding:0; }

body { background:url(i/bg.jpg); }

#container { width:960px; margin:0px auto; }

h2 { font-weight:100; text-shadow:#ffffff 1px 1px 0px; text-align:center; padding:20px; font-size:32px;  color:#555555; border-bottom:1px dashed #ccc; margin-bottom:30px;  font-family: Georgia, "Times New Roman", Times, serif ;  }

First, we have created a 960px centered container, and have added a background to the page. Additionally, we’ve added some basic styling to the title ( h2 tag ).

Styling the Gallery

Next, we’ll style the gallery and, while we are at it, throw in some CSS3 goodness.

#gallery {
list-style:none;
}

#gallery li {
background:#e8e8e8;
float:left;
display:block;
border:1px solid #d7d7d7;
-moz-border-radius:4px;
-webkit-border-radius:4px;
border-radius:4px;
-webkit-box-shadow:1px 1px 6px #ddd;
-moz-box-shadow:1px 1px 6px #ddd;
box-shadow:1px 1px 6px #ddd;
margin:15px 56px;
padding:0;
}

#gallery li p {
border:1px solid #fff;
-moz-border-radius:4px;
-webkit-border-radius:4px;
border-radius:4px;
margin:0;
padding:7px;
}

#gallery li a {
display:block;
color:#fff;
text-decoration:none;
padding:0;
}

#gallery img {
width:315px;
height:210px;
margin:0;
padding:0;
}

At this point, our gallery should look like so:

image1

Setting up the Preloader Class

Let’s create a preloader class that will be responsible for showing the loading icon, while images are loading.

.preloader { 
   background:url(i/89.gif) center center no-repeat #ffffff; 
   display:inline-block;  
}

The preloader element’s display property must be set to block or inline block; otherwise, the loading icon won’t show.


Step 4: Writing the Plugin

Let’s begin by creating the plugin structure and options.

Allowing for customization options makes a plugin far more flexible for the user.

We start off with the base structure:

$.fn.preloader = function(options){
	
	var defaults = {
		 delay:200,
		 preload_parent:"a",
		 check_timer:300,
		 ondone:function(){ },
		 oneachload:function(image){  },
		fadein:500 
	};
	
	// variables declaration and precaching images and parent container
	 var options = $.extend(defaults, options),
     
     }

Our Options

  • delay – Successive delay between fading in images
  • preload_parent – Add preload class to the parent mentioned. If not found, the image is wrapped within an anchor tag
  • ondone – Callback to be executed when all the images are loaded
  • oneachload – Called when each image is loaded with image as the parameter
  • fadein – Fade in animation duration

Step 5: Variables

Next, we declare and precache the variables that we will be using in the rest of the plugin.

	var defaults = {
		 delay:200,
		 preload_parent:"a",
		 check_timer:300,
		 ondone:function(){ },
		 oneachload:function(image){  },
		fadein:500 
	};
	
	// variables declaration and precaching images and parent container
	 var options = $.extend(defaults, options),
	       root = $(this),
               images = root.find("img").css( {"visibility":"hidden", opacity:0} ),  
               timer,  
               counter = 0, 
               i=0 , 
              checkFlag = [], 
              delaySum = options.delay;

First, we precache the root element (always a best practice), then find the images (also making them hidden), and finally declare the variables which will be explained in greater detail as we counter them.

There are two things worth noting here: you might initially think the easiest solution is to hide the images, and then fade them in, rather than jumping through all of this code. However, the problem is that, if we hide the images, the browser marks the space they used to occupy as empty, and thus the layout, itself, is messed up when they’re eventually faded in. Okay, well what if we used opacity to “show” and “hide” the images? That’s a better practice, though, some versions of IE doesn’t like this method.


Step 6: Adding Preloader Class

We now will iterate over each image element, and check if its parent is the one mentioned in the option. If so, we add our preloader class to it; else, we wrap the image within an anchor tag with a class of preloader.

images.each(function(){
	var $this = $(this);
	if( $this.parent( options.preload_parent ).length==0 ) {
	   $this.wrap("<a class='preloader' />");
        } else {
	   $this.parent().addClass("preloader");
       }
		
      checkFlag[i++] = false;		
}); 
images = $.makeArray(images); 

Here, we are using an array checkFlag, and are setting each array’s item value to false. Its use will be made clear as you move along.


Step 7: Bringing it All Together

We’ll now implement what actually happens behind the scenes. There is a boolean property, called complete, associated with the image object. When the image has been loaded completely, this boolean is set to true. So, we keep checking this property for each image, and, if it is indeed set to true, we fade in that image.

We can use the setInterval function to continuously determine whether the images have been loaded or not. This is where the check_timer option comes in: it maps directly to our timer’s frequency.

An image also has an onload event associated with it; you’re probably wondering why we aren’t using it. The reason is because some browsers don’t work well with that event; as such, we’re skipping it. We need a solution that works like a charm across all browsers. We start off with:

 init = function(){
	timer = setInterval(function(){}		
},options.check_timer);

timer is the variable which will reference the timer. This is needed in order to eventually stop the timer. This function is declared along with all the variables.

Checking Each Image

We’ll iterate through the array and check each image’s complete property to determine whether it has finished downloading. If it has been downloaded, we will set it to visible and fade in slowly. When the animation has ended, we remove the preloader class from its parent.

for(i=0; i<images.length; i++) {
	if(images[i].complete == true) {
    	$(images[i]).css("visibility","visible")
    	   .delay(delaySum)
    	   .animate({opacity:1}, options.fadein, function(){ 
    	   		$(this)
    	   		   .parent()
    	   		   .removeClass("preloader");
    	   	}); 
		}
	}

There’s a tiny issue here: the timer will continue to check — even after the images have all been loaded.To counter this, we’ll add a counter variable, and increment it after each image has been loaded. This way, we can check if the counter variable is equal to the size of the images array. If that’s the case, we stop.

timer = setInterval(function(){
	if(counter>=checkFlag.length) {
		clearInterval(timer);
		options.ondone();
		return;
	}
            
	for( i=0; i<images.length; i++) {
		if(images[i].complete==true) {
	        $(images[i])
	           .css("visibility","visible")
	           .delay(delaySum)
	           .animate({opacity:1}, options.fadein, function(){ 
		           $(this)
		              .parent()
		              .removeClass("preloader");
		        });
						
			counter++;
		}        
	}
			
},options.check_timer) 

However, there’s another small issue now. Our timer may stop earlier than expected; if one image has been loaded, its complete property has been set to true and the counter thus increments by 1. Now, when the loop runs the next time, the image is already loaded, the complete property is set totrue, and, thus, the loop will run twice! To overcome this problem, we use the checkFlag array. When an image is loaded, we will set checkFlag to true, and set the condition for the counter to increment only on the condition that the checkFlag value is false. So counter is incremented only once: when an image is loaded for the first time.

timer = setInterval(function () {

    if (counter & gt; = checkFlag.length) {
        clearInterval(timer);
        options.ondone();
        return;
    }

    for (i = 0; i & lt; images.length; i++) {
        if (images[i].complete == true) {
            if (checkFlag[i] == false) {
                checkFlag[i] = true;
                options.oneachload(images[i]);
                counter++;

                delaySum = delaySum + options.delay;
            }

            $(images[i]).css("visibility", "visible").delay(delaySum).animate({
                opacity: 1
            }, options.fadein, function () {
                $(this).parent().removeClass("preloader");
            });
        }
    }

}, options.check_timer);

Note that we call the ondone function when the counter flag is greater than the array’s length – i.e. when all the images are loaded. When the counter is incremented, oneachload is called with the current image passed as the parameter.


Step 8: The Easy Part

Finally, in this step, we call the init(); function at the end of the plugin.

init(); // called at the last line of plugin

That’s all; we have made a fully working preloading plugin, and its size is less than 2kb. Still, one problem remains: the loading icon image is loaded randomly. We don’t want that. In the next section we will take care of that.


Step 9: Going the Extra Mile

To fix the problem mentioned above, we’ll load the icon first, and then call the init function. But the loading icon is a background image, so we inject it as an image in the page, while keeping it hidden. When it loads, we call the init function. We’re essentially preloading the icon itself.

var icon = jQuery("<img />", {

    id: 'loadingicon',
    src: 'css/i/89.gif'

}).hide().appendTo("body");

timer = setInterval(function () {

    if (icon[0].complete == true) {
        clearInterval(timer);
        init();
        icon.remove();
        return;
    }

}, 100);

First we create an image object with an id of loadingicon, and a source pointing to the path of the loading icon. Then, we append it the body and initially hide it. Lastly, we set the interval to check if the icon has been loaded or not. If it has, we kill the timer, and start preloading the images. Don’t forget to remove that icon too!


Conclusion

With that last step, we’re done! This functionality works in all browsers, just as expected, and degrades gracefully. Just be sure set the preloader element’s display property to block or inline-block. It is now ready to be used in your projects. Thanks for reading!

Tags: jQuery
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.quicoto.com quicoto

    Great post!

    The preloader is quite light. I would paste it somewhere else to avoid an extra request (jquery + preloader).

    Regards

  • http://www.jeffadams.co.uk Jeff Adams

    Many thanks for this. My next template over at Themeforest has alot of icons, it’d be very nice for them all to appear gracefullly since at the moment its dependant on the clients internet speed.

    Neat tut!

  • Thiago

    Thanks for the tutorial! :)

    Can I use it to preload a large background image?

  • http://www.dimensionedelta.net/html5/ Francesco

    Wonderful, thanks! I’d been looking for something like this for a while, now.

    PS. Since you’re clearly using an HTML5 Doctype (great!), why don’t you just get rid of those useless ‘type=”text/javascript’” and ‘type=”text/css”‘?

  • http://microtwitt.com Alexander

    How can I add the preloader to more then one tag?

  • http://www.impulsis.com/blog.html Wolf

    Wow! Looks cool. Thanks, Abhin.

  • Ezequiel

    Great tutorial, i see a blue border in IE7 seems too have border in img element.

    • http://angusfretwell.com Angus Fretwell

      Just add a img {border:none;} to the CSS to remove the blue border ;)

  • http://moniestudios.com Monie

    Great tips. I’ll be using this in my current project!
    Thanks.

  • Edward Longman

    Like it but its a but pointless in my opinion and also the loading GIF runs slowly when the page is loading
    otherwise good idea and useful.

  • http://www.dazzlecat.co.uk DazzleCat Digital Agency

    Already found a use for this on a new project.

    Thanks.

  • http://www.waldson.com.br Waldson

    Great Plugin.But you have to fix a little error on line 4 of section “Adding preloader class”: Use options.preload_parent instead “a” tag.

    $this.wrap(“<” + options.preload_parent + ” class=’preloader’ />”);

    Thanks.

  • http://www.graphicom.ca Guillaume

    This is great! I was just looking to achieve something like this.

  • http://www.marioawad.com Mario Awad

    Great article. Thanks. Quick tip: a loading CSS animation will definitely be much better than the loading GIF as that also takes some time to load… Hopefully CSS animations will soon be more widely supported… Cheers :D

    • http://dharyk.net Miguel Guerreiro

      Instead of a CSS animation which is not quite supported throughout most browsers (yet, anyway), you could insert the loading icon as a data url straight into the CSS file.
      Yes it adds a few extra kb to the CSS, but it does remove the need to load the image separately.

  • http://www.jakswebdesign.com Adam

    Nice article. I was completely unaware of the ‘complete’ property of images. I’ve built similar (but simpler) preloaders prior to this that accomplish the same thing but I was always using jQuery’s $.load functions resulting in extra unnecessary server calls – thanks for opening my eyes to something I probably should have known about for years!

  • http://brianegan.com Brian Egan

    Hello there!

    I liked the plugin, but thought it might work better by relying on simple “load” events. I thought I’d have a bit of fun and rewrite the plugin to use load events instead of timeouts. Here’s my demo:

    http://fiddle.jshell.net/MBV2X/87/show/light/

    And you can view and edit the source here:

    http://jsfiddle.net/MBV2X/87/

    Cheers!
    Brian

  • Marco

    Don’t get me wrong, but neither does this preload anything, nor does it greatly boost performance – if anything. It’s a nice visual effect though..

  • Christophor S. Wilson

    It has a very flash like feel to it, I love it, great tut!

  • http://www.akeanant.com akeanant

    Very nice solution.

  • http://emanuelesala.com Em

    Great just added to my new project !!! Thanks for sharing !

  • Jake

    Very useful, thanks for sharing!

  • cuginoAle

    I have to agree with Marco; it’s not a preloader, it’s a “display this animated-Gif before this other image is available” function, hence my question: why don’t just set the background-image of the .
    Something like:

    img.preloader{ display:block; width:300px;height:150px;background-image:url(../img/loading.gif)};

    Once the real image is loaded it will hide the background one. Am I wrong??

    • http://weekdayheroes.co.za RiaanP

      Yep, that’s how I solved it too. I tried using javascript load functions, but it wasn’t that intuitive for me (i.e., I don’t know how they work) so I settled for the BG image solution. same same. :)

      The main difference would be the fading up effect. You can’t achieve that without using the above JS implementation. (as far as I know)

  • http://newdailyblog.blogspot.com Tahsin Hasan

    awesome article, thanks for the post. find many web articles on tahSin’s gaRage.

  • http://www.manakmichal.cz manakmichal

    Does it really work in Internet Explorer? I have a problem to preload images in this browser (IE 7/8/9) but in other browsers it works great. Where can be a problem? I use your default sources to test it instead to write my own. But i would like to thank you for this smart preloader.

  • # nash

    Hi everyone, im new to jquery and i know almost nothing about javascript. :(

    i have these lines of code that just wont work for me…

    My problem is that it doesnt fade in…
    Can anyone have a look and tell me whats wrong??? PLEASEEEE!

    Code:

    $(#secondary_content_what_we_do li a).click(function(){

    var url = $(this).attr(href);

    $(#loading_content).load(url);
    (function (e) {
    $(this).hide().appendTo(#loading_content).fadeIn(1000);
    e.preventDefault;
    });
    return false;
    });

    I also would like to add a preloader so that people know that the content is loading.

    Thanks :)

  • http://rayvellest.com Ray Vellest

    Brilliant! I was just looking for it. Thanks for sharing.

  • http://www.sitebase.be Sitebase

    This kind of small things makes the web so much more attractive.
    Good job!

  • MC

    Nice tut.

    Though I have to disagree with the whole “graceful loading” and “makes the web more attractive”. I find this extra bit of ‘functionality’ is really unnecessary and frustrating. It just makes things load slower, I visit psdtuts and tutorials with 20-30 images with this “preloader” crap takes a bloody long time to load. I’m half way down the page the images are still ‘loading’.

  • Chris

    Issue with IE (7+8) basically they are not appearing…

    the developer tools shows that the inline style on the img is remaining:

    visibility: hidden,
    filter: alpha(opacity=0);

    therefore not showing the image.
    any help on this, much appreciated – it’s the only preloader that makes good sense!! :-)

  • Chandan Chakraborty

    I am looking for this to web more than 2 month but not seem. Thanks Abhin for this nice tutorials its make to html website more attractive :)

  • Chris

    Seem to be having issues implementing lightbox to each image after the “load”… Anyone else try this?

  • http://www.xisvi.com xisVi.com

    Awesome! I added this to my website (which i’m currently working on).

  • http://www.rahulsahu.com/ Web graphic designer, web designer, logo design india

    wow looking good.

  • Jere

    Cant get this work on IE8 & 7, any help?

  • Jack

    Dosn‘t work on IE 8 & 7. Please help!

  • Reda

    Hello everybody and happy new year!

    Thanks Abhin for this nice plugin.
    I have 1 question and 1 problem :)

    - question: Do I have to put the images in ?? i’d like to use something like

    - problem: i’m using transparent png images that contain external shadow done with Photoshop. ie 7 & 8 show an ugly black shadow. do you know why?

    Thank you so much

  • Reda

    Hello everybody and happy new year!

    Thanks Abhin for this nice plugin.
    I have 1 question and 1 problem :)

    - question: Do I have to put the images in the unordered lists?? i’d like to use divs

    - problem: i’m using transparent png images that contain external shadow done with Photoshop. ie 7 and 8 show an ugly black shadow. do you know why?

    Thank you so much

  • Өлзийбат

    thanks for sharing awesome script.

    i want to use on http://neteye.github.com/activity-indicator.html for image preloader. How can i use this. Please give me direction.

    Thanks

  • Ezzo

    Pls, can someone help me out? I want to use the prolaoder code on Pirobox gallery page. it ends up disabling the pirobox effect even the images too do not preload.

    I believe there might be a conflicting attribute can anyone advise.

    Thanks

  • Gordon

    Thanks for the helpful tutorial:)
    It’s working fine on all browsers if it’s static content like your downloadable sample demo but when it comes to wordpress, IE8 (maybe IE7 also: I haven’t tested yet) won’t show images but just showing loading.gif…

    Probably people who mentioned “doesn’t work in IE7, 8″ might have the similar situation like me.
    Do you have some magical countermeasure code for this?

    I want to use your plugin!!

  • decknology

    useless on wordpress site. ie8 and ie7.

  • Gordon

    >>decknology and others who mentioned that IE7 and 8 doesn’t display images actually it’s all working fine!
    I was one of them tying to use it inside wordpress but didn’t work though works without wordpress…
    It took about 10 hours to work out what was wrong, then I found the solution!!!!

    Please use absolute path if you are using WordPress.
    Around 70th line inside the “JS file” there is a section for setting path to the loading gif image.
    It’s relative path in the file originally (if you downloaded the source file) so this needs to be changed to “absolute path” to the loading image. Mine for example, I set like below…

    src : ‘/wordpress/wp-content/themes/mytheme/images/loading_s.gif’

    Please make sure to use the same name of loading gif image as one inside css file which defines for the background of the class “preloader”

    Thank you very much for such great plugin, that’s what I have been looking for!!

    • Sam

      Nice work, cheers buddy, solved the IE7 and 8 issues.

  • Rics

    Hi!
    I’m trying to use this script with wordpress but it don’t work really well with it:
    when a page is loaded, the images aren’t “preloaded” but loaded in a normal way, then, after this, the images disappears to re-appear with the preload effect.

    Seems that the effect apply his function after the page and images load.

    I have some custom mysql queries into the wp template pages that are calling the required data for showing the images list, i don’t know if this can bring interfererences with the preloader script.

    In the meatime thank you for this piece of code anyway!

  • http://www.inkfreakz.com/ Inkfreakz – Tattoo Design

    Great Post. thanks for share :)

  • http://www.imstillreallybored.com Josh Bedo

    I can’t seem to get the preloader image to center inside of images it always goes completely to the bottom anyone know why? I tried adding display:block to the element and parent elements and it does nothing. I’m currently testing in Firefox 3.6 and IE8.

  • http://www.brettwidmann.com Brett Widmann

    This was a really helpful tutorial! Thanks for sharing.

  • Rics

    Ahahah, i got the exact opposite, completely to the top!

  • http://www.kredit-ohne-schufa.co.cc/ Kredit

    I’m often to running a blog and i really recognize your content. The article has really peaks my interest. I’m going to bookmark your site and keep checking for brand new information.

  • http://distractionphotography.co.uk/ Mark

    I love this preloader.

    As usual with everything I do, I seem to have a small problem though!

    On occasion, the images actually load FIRST then the preloader overlays and then fades out as usual.

    I have a feeling that it could be a script order issue but I swear I have the css and js in the best order possible!

    I can’t seem to replicate it, it just happens on occasion and it’s more prone to happen in IE8/IE9.

    It sort of makes it a bit redundant so any help would be very appreciated, thanks!

    • http://www.sahilmepani.com Sahil Mepani

      I’m facing the same problem. In firefox and other browsers as well.

      I’ll really appreciate, If someone can solve/fix this.

    • http://www.sahilmepani.com Sahil Mepani

      Hi, I found the fix.

      Just add CSS visibility: hidden to the Image you are preloading

  • Frank

    I think that this one is The most cool thing I’ve ever seen – http://core-framework.googlecode.com/svn/examples/loader.html

  • http://www.a-d-g.net Phil Shields

    Looks great! I’d really like to use this for my website, but my pages all load with AJAX, and I can’t get it to work, do I have to call a function after the page data loads, and if so…what function?….sorry newbie question,

    • http://www.a-d-g.net Phil Shields

      I got it to work, for the benefit of anyone else who is trying the same thing…I rapped the code in a function, and called it using a poll, with code from dynamicdrive…http://www.dynamicdrive.com/forums/archive/index.php/t-13003.html ….loading this way the fixes above are also needed to make it work properly…..