Search

Image Loading

Posted on 4th February 2008 — Daniel Mee requested a tutorial and writes:

I have a large image (500k). I have a loading gif (little rotating circle thingy from ajaxload.info). I want the animated gif to be swapped with the large image once it’s loaded. The event may be on page load or may be some button onClick…

This is similar to how a LightBox would work, except Daniel wants complete control of the the load event.

This tutorial will show how to load images in the background, and once loaded handle the event and create your own response.

Basic Version

In our basic version, we will have a single div containing a loading spinner and once our large image is loaded (in the background) we will remove the spinner and insert our image.

There’s a few ways to approach the loading screen, two of which are:

  1. Use a background image on the holder div, this way we can easily centre align horizontally and vertically using CSS, rather than adding extra markup.
  2. Adding a styled div in our holder div, then remove the entire block of markup when the image loads.

I’ve provided a screencast explaining how to achieve this (though it is based on the CSS version, also shows how to do this with a separate loading div).

Watch the jQuery basic image load screencast (alternative flash version)

View the demo and source code used in the screencast

Note that in the demonstration as I am simulating loading a slow to load image by including a script tag at the bottom of the markup. In your real world version, you obviously would not include it.

HTML Markup

The markup (segment) that we’re using is extremely simple. Note, however, I’ve included a ‘loading’ class on the div. This will be manipulated in our jQuery once the image has loaded behind the scenes:

<div id="loader" class="loading"></div>

CSS

For the demo, I’ve set the height and width of the empty div, but also included a background image which indicates something is loading.

You can use any kind of image - I’ve seen applications use much larger animated gifs to simulate a loading message box.

DIV#loader {
  border: 1px solid #ccc;
  width: 500px;
  height: 500px;
}

/** 
 * While we're having the loading class set.
 * Removig it, will remove the loading message
 */
DIV#loader.loading {
  background: url(images/spinner.gif) no-repeat center center;
}

jQuery

The jQuery’s job is to:

  1. Load the image in the background
  2. Hook a load event
  3. Once the image has loaded, strip the loading class and insert the image
// when the DOM is ready
$(function () {
  var img = new Image();
  
  // wrap our new image in jQuery, then:
  $(img)
    // once the image has loaded, execute this code
    .load(function () {
      // set the image hidden by default    
      $(this).hide();
    
      // with the holding div #loader, apply:
      $('#loader')
        // remove the loading class (so no background spinner), 
        .removeClass('loading')
        // then insert our image
        .append(this);
    
      // fade our image in to create a nice effect
      $(this).fadeIn();
    })
    
    // if there was an error loading the image, react accordingly
    .error(function () {
      // notify the user that the image could not be loaded
    })
    
    // *finally*, set the src attribute of the new image to our image
    .attr('src', 'images/headshot.jpg');
});

Added Functionality

The jQuery code used is the constructs of our load function. If we wanted to style the image in any way, add classes or run an Ajax request before showing the image, it would go inside the .load() function.

This functionality could also be placed inside of a .click() event handler or anything else if you wanted to trigger the image loading.

For example, if you had a particular image map linked to this image that you wanted to request via Ajax the contents of the load function would be this:

$(img)
  .load(function () {
    $(this).hide();
    
    // our bespoke ajax hit that's required with the image
    // it will return the HTML for the <map> element and
    // linked <area> element.
    $.ajax({
      url: 'image-map.php',
      data: 'img=' + i.src, // the image url links up in our fake database
      dataType: 'html',
      success: function (html) {
        // because we're inside of the success function, we must refer
        // to the image as 'img' (defined originally), rather than 'this'
        $('#loader')
          .removeClass('loading')
          .append(img)
          .append(html);
          
        // now show the image
        $(img).fadeIn();
      }
    });
  })
  // code continues as before

Related posts

Demo

If you find this demo doesn't work as expected, it's possibly due to the demo running from within an iframe. Try running the demo in it's own window.

Source Code

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>Image Loading</title>
    <style type="text/css" media="screen">
    <!--
        BODY { margin: 10px; padding: 0; font: 1em "Trebuchet MS", verdana, arial, sans-serif; font-size: 100%; }
        H1 { margin-bottom: 2px; }

        DIV#loader {
            border: 1px solid #ccc;
            width: 500px;
            height: 500px;
            overflow: hidden;
        }

        DIV#loader.loading {
            background: url(/images/spinner.gif) no-repeat center center;
        }
    -->
    </style>

    <script src="/js/jquery.js" type="text/javascript"></script>

    <script type="text/javascript">
    <!--
    $(function () {
        var img = new Image();
        $(img).load(function () {
            //$(this).css('display', 'none'); // .hide() doesn't work in Safari when the element isn't on the DOM already
            $(this).hide();
            $('#loader').removeClass('loading').append(this);
            $(this).fadeIn();
        }).error(function () {
            // notify the user that the image could not be loaded
        }).attr('src', 'http://farm3.static.flickr.com/2405/2238919394_4c9b5aa921_o.jpg');
    });
    
    //-->
    </script>
</head>
<body id="page">
    <h1>Image Loading</h1>
    <p>This demonstration shows how to pre-load an image in the background while showing a loading screen.  It also supports loading the image, then running additional code, such as Ajax requests before finally showing the image.</p>
    <p><a href="http://jqueryfordesigners.com/image-loading/">Read the article this demonstration relates to</a></p>
    <div id="loader" class="loading">

    </div>
    <p><a href="http://www.flickr.com/photos/remysharp/">More photos...</a></p>

    <!-- this is just a way to simulate a delay (the script will sleep for 2 seconds then response) -->
    <script src="image-load-demo.php?action=delay" type="text/javascript" charset="utf-8"></script>
</body>
</html>

Comments

  1. Scott Mackenzie On 18th September 2009 at 08:09

    Thanks for this! Easy to follow. Simple to execute.

  2. HappyPod On 13th October 2009 at 22:10

    Related to this topic — I have a page that:

    • Has a button whose background image will change upon rollover (done in CSS, using “:hover”)
    • Preloads the rollover image for the button above (done using a simple jQuery script)

    The page can be found at http://test.happypod.com/preloadimage/

    The part that I don’t quite understand is that when I inspect the page using Firebug (Net tab):

    • Two images are loaded upon page load: signupro.png and signup.png. This is expected and shows that the jQuery script successfully preloaded the rollover image, signupro.png
    • When you hover your mouse over the button, there is another request to fetch signupro.png although it is clear that that image has been preloaded.

    Why does the browser fetch signup_ro.png again although that image has been preloaded? For screenshot, see: http://test.happypod.com/preloadimage/firebug.png

  3. David On 2nd November 2009 at 22:11

    Hi,

    thank you for this great tutorial. Could you just tell us how to stop the loading? Because I have to override the loading, and I can’t stop the first load. So my complete callback are called 2 times and not 1….

    thank you for your help!

  4. Kevin On 26th November 2009 at 22:11

    Hey - thanks for this post! I kept thinking, “isn’t load only for AJAX?” and thought I was going to have to do up a whole AJAX handler in my PHP script just to swap images on the fly (I would just return the proper DOM element and swap it in…) - but this saved me the time and worked like a charm.

    I hear that swapping images is territory to tread lightly when it comes to Webkit browsers, but I’ll cross that bridge when I come to it…it’s good for now :D

    Take care.

  5. Mike On 2nd December 2009 at 03:12

    Excellent! Solved the exact problem I was having. Thanks for posting this!

  6. Benedict Raynes On 7th December 2009 at 18:12

    This is strange - this method works very well on a apache server located on my computer, but when I tried deploying a website written with this code on any hosting, outside server - the images either didn’t show up at all or showed up severely distorted. Do you know what could be wrong?

  7. estevan On 18th January 2010 at 09:01

    Any suggestions on how I can implement this code without designating the image src in the javascript? I’m trying to set something up like this in Wordpress and I want it load the img from the PHP.

  8. Kyle On 18th January 2010 at 13:01

    Hi,

    First and foremost this is a great piece of work you have provided us with, thanks very much.

    I was wondering if you could or someone could help me with one problem Im having, I cant get the image fade to work on this page http://kylehouston.com/new/index_new.html everything else works fine.

    Thanks in advance

    Kyle

Leave your own comment
  • http://