Load images with jQuery Deferred

Have you ever needed to know the dimensions of an image in JavaScript? In the past, you may have used code like this:

var image = new Image();
image.onload = function() {
  alert("Loaded image: " + image.width + "x" + image.height);
};
image.src = "/my-image.png";

This is okay for very basic scenarios, but it becomes tricky to handle things like:

I also dislike the backwards nature of writing the callback function before assigning the src property. This is essential however, because image loading is asynchronous and could potentially finish before your callback was assigned to onload.

Fortunately, there is a cleaner way to handle this.

Using jQuery Deferred

The jQuery deferred object is perfect for wrapping up asynchronous operations, such as image loading. The deferred object provides a consistent API for adding callbacks and chaining operations. It results in very readable code.

Here's what it's like to load an image using a jQuery deferred object.

$.loadImage("/my-image.png")
.done(function(image) {
  alert("Loaded image: " + image.width + "x" + image.height);
})
.fail(function(image) {
  alert("Failed to load image");
});

jQuery deferred's are easy to chain together using the pipe function. For example, imagine we need the dimensions of a user's avatar image. But first we need to the image URL from a profile retrieved via ajax...

var avatarDimensions = $.get("/user-profile")
  .pipe(function(profile) {
    return $.loadImage(profile.avatarUrl);
  })
  .pipe(function(avatarImage) {
    return { width: avatarImage.width, height: avatarImage.height };
  });

// avatarDimensions is a deferred object.
avatarDimensions.done(function(dimensions) {
  console.log(dimensions);
});

Implementing $.loadImage

Here's the code for $.loadImage. It assumes you have already included jQuery 1.5 or newer in your page.

$.loadImage = function(url) {
  // Define a "worker" function that should eventually resolve or reject the deferred object.
  var loadImage = function(deferred) {
    var image = new Image();
    
    // Set up event handlers to know when the image has loaded
    // or fails to load due to an error or abort.
    image.onload = loaded;
    image.onerror = errored; // URL returns 404, etc
    image.onabort = errored; // IE may call this if user clicks "Stop"
    
    // Setting the src property begins loading the image.
    image.src = url;
    
    function loaded() {
      unbindEvents();
      // Calling resolve means the image loaded sucessfully and is ready to use.
      deferred.resolve(image);
    }
    function errored() {
      unbindEvents();
      // Calling reject means we failed to load the image (e.g. 404, server offline, etc).
      deferred.reject(image);
    }
    function unbindEvents() {
      // Ensures the event callbacks only get called once.
      image.onload = null;
      image.onerror = null;
      image.onabort = null;
    }
  };
  
  // Create the deferred object that will contain the loaded image.
  // We don't want callers to have access to the resolve() and reject() methods, 
  // so convert to "read-only" by calling `promise()`.
  return $.Deferred(loadImage).promise();
};

Would you like access to the sample code repository?

Subscribe to my web development mailing list.

No spam, just occasional web development tips, posts and sample code. Unsubscribe at any time.

Comments
blog comments powered by Disqus