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
  • Abraham Shakir Torres

    How can I make it works when I load images with php?

  • Jan Banan

    If someone knows a way to work around previously mentioned problem with “dynamic content” in WordPress for IE i’d be grateful if you could share it. I’ve tried changing the path to the root folder but it still doesn’t work.

  • http://kreont.com Pavel

    //Going the Extra Mile in WordPress Preloader Fix for IE
    var loadingiconsrc=”;

    • http://top-wordpress.net Mr Pixel

      If you would be more explicit would really help
      Thanks

  • http://kreont.com Pavel

    //Going the Extra Mile in WordPress Preloader Fix for IE
    var loadingiconsrc=”;

  • Chaitanya Bijawe

    how to insert buffer image instead of default buffering image in html 5 video tag

  • Axel Ferdinand

    AMAZING tutorial…. Solved my problem!

  • http://www.nonstopas.no/ny/ Axel Ferdinand

    Is there a way to add text (a caption, sort of) to the images? Ive been trying 50+ jquery scripts, but most of them uses absolute positioning of the images, something that crashes with my 100% width on images. Take a look at the link I posted, an se how the images are scaleable an works perfektly. Just wondered if its possible to add some text…

    Thanks!

  • Yuvraj Singh

    thnxx a lot helped me a lot :)))) Big thnxx

  • Twelve

    Hey there, I’m having difficulties making this work for Versions 5.0 and 7.0 of Firefox OS X through testing on Adobe’s BrowserLab. Any ideas?

    Thanks.

  • http://www.jayseventwo.com jayseventwo

    Hi guys – i too am having trouble getting it to work in IE, any help appreciated as this is an awesome looking script.

    To anyone wanting to use it stripped back and not within a list, this is what I have done:

    In your css file:

    #gallery { float:left; }
    #gallery a { display:block; }
    .preloader { background:url(../images/preloader.gif) center center no-repeat; display:inline-block; }

    In your HTML:

    This works great in all browsers EXCEPT IE – loading gif just stays loading and does not show preloaded image.

    • http://www.jayseventwo.com jayseventwo

      Sorry, for some reason my HTML code didn’t display:

      In your HTML code:

      <div id=”gallery”>
      <img src=”images/property.png”>
      </div>

  • http://www.jayseventwo.com jayseventwo

    Ok, after much mucking around i have got it to work in IE, and it ws a simple solution!

    In the css file, the link to the preloader.gif is relative to the .css file and in the preloader.js file the preloader.gif is relative to the html document. Thats it (hopefully)! :)

    • Chris

      Could you explain it more clearly? I have the same problem and I’m new to css. Thanks

      • taylor

        IE 7, 8 & 9 loader Issue…….. Make sure to update the the path for loader.gif in jquery.preloader.

        I hope this helps the next guy

  • Mosh

    Internet Explorer Issues:

    I’m using WordPress and I cant see My post who had the preloader.

    Is any body can help me?

    Thank, Mosh

  • Hasan Akdas

    I want to load images ordered. For example, i have 5 images (a,b,c,d,e) and web page
    load order should be:

    a: 1
    b: 2
    c: 3
    d: 4
    e: 5

    Could you add load order to this script?

  • http://www.grafikkid.com Chris Stauffer

    Any way to apply this script to the ENTIRE website? And preload everything?

  • sk

    Awesome! There are many preloading tutorials on the web, and this is by far the simplest and nicest way to get the job done.

    Took me about 20 mins to implement it on this WordPress site http://www.pdt.com.au

    Thanks again nettuts!!

  • Jon

    I had an issue in IE9 where the preloader image wouldn’t go away. So after a bit of toiling and knowing the images were loaded, I changed line 80 in jquery.preloader.js from true to false so the timer would force the preloader image to disappear regardless. Not ideal, but my images are fairly well optimized so it’s a decent band-aid for now.

    Thanks for the script!

    • Willian

      Here happens the same. IE9 doesn’t show the image loaded. Just stuck in preloader image.

  • Thomas

    Nice to see a preloader that works with internet explorer :-)

  • http://handhugs.com Jessie

    This doesn’t work with the most recent version of jQuery – any amendments??

  • Daniel

    This post is really great, I’m trying to use this in my own site, although I’m having a bit of bother because it won’t work on images that are being pulled from flickr, I think the code is running before the flickr photos are loaded into the DOM. Could anyone help with this?

  • Noah

    Nice but it seems to take an age for the images to load?

  • taylor

    IE 7, 8 & 9 loader Issue…….. Make sure to update the the path for loader.gif in jquery.preloader.

  • http://twitter.com/jesu_carrasco Jesus Carrasco

    Hey, great post. Thanks a lot

    I had several galleries (several ”) listing different image directories in my page and this solution only worked on the first gallery of all. This is easily fixable by selecting galleries this way:

    $(“ul[id=gallery]“).preloader();

    Just in case somebody bumped with the same rock.

    Thanks

  • taqqiyah

    this is not preload!!!
    it just hiding image (and showing animated loading) and showing image if its completely loaded!

    • TheBasti82

      hidepreloading? :D