Advertisement
JavaScript & AJAX

How to Create an Awesome Image Preloader

by

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!

Related Posts
  • Web Design
    UX
    Impress Your Visitors With a Web 2.0 Hit CounterCounter thumb
    If there's one way to show off how successful your site is, it's by letting your visitors know how many others have been there before them. Let's furnish a web page with a hit counter!Read More…
  • Web Design
    Complete Websites
    Building the Responsive Timeline Portfolio PagePortfolio thumb
    During this tutorial we will be building the fantastic Timeline Portfolio as seen in an earlier tutorial by Tomas Laurinavicius. We will be using some responsive techniques as well as CSS3 animations, Sass and a little bit of jQuery.Read More…
  • Web Design
    HTML/CSS
    Build a Top Bar Off-Canvas Navigation With Foundation 5Foundation menu
    Today, we are going to combine ZURB's Foundation 5 Off-Canvas feature with our top bar navigation. The result will be a nice custom navigation for desktop users and a sleek off -canvas menu for tablet and mobile users. Read More…
  • Web Design
    Case Studies
    How They Did It: Typekit's New HomepageTypekit retina
    Typekit recently redesigned their homepage with some new services in mind. When Typekit joined Adobe, they set out to bring us a new way to handle fonts on the web. Not only did they create a fairly simple way to embed fonts on the web, but they have now officially launched a desktop sync option, which allows Creative Cloud subscribers to sync fonts to their computer directly from Typekit. This has been in a beta form for a while now, and provides a much easier route to local fonts than finding them elsewhere!Read More…
  • Web Design
    UX
    Walk Users Through Your Website With Bootstrap TourTour retina
    When you have a web application which requires some getting used to from your users, a walkthrough of the interface is in order. Creating a walkthrough directly on top of the interface makes things very clear, so that's what we're going to build, using Bootstrap Tour.Read More…
  • Code
    PHP
    Creating a Photo Tag Wall With Twilio Picture Messaging & PHPProcedural to oop php retina preview
    Twilio's recently announced Picture Messaging has vastly opened up what we can do with text messaging, now we can attach photos to our text messages and have them get used in different ways. In our case, we are going to build a Photo Tag Wall, which will contain photos linked to tags that will be displayed on a website.Read More…