Anthony McLin

On-Demand Image Loading with JQuery

Q: How do you load up a bunch of images in a slideshow without causing long page load times?

A: Load the images on-demand. Here's an example of how to load images on demand when using JQuery Cycle to run a slideshow.


Many times I've needed to load a large quantity of high-resolution images into slideshows or carousels with custom functionality, which leads to problems with loading times. On this client's site I solved the problem with a custom-built image loader using MooTools. In my solution, upon javascript load, all the necessary images had their source changed to a loading image. Once the image was needed (in this case, scrolled onto screen) the image source was changed back to the original, which triggers the loading.

The nice thing about this solution, is that it still works if Javascript is disabled. It may not pretty, but at least your images display.

I ran into the need for an image loading solution again recently, but this time, I was working on a site using JQuery, not MooTools. Since JQuery has a rich library of plugins, I figured there must be something out there ready to go. I was disappointed in what I found. One popular solution, LazyLoad, was first put out in 2008. Apparently though, it doesn't work right, and there is no-one maintaining it to work with newer browsers. The next solution I dug into looked quite promising. JAIL does work well and seems to have active support, but it doesn't work with Javascript disabled. It requires that you modify your HTML to point your images at a dummy source, and hide the real source in an alternate attribute. Unfortunately, if Javascript is disabled, your images will never actually display.

So here's my solution which is a rewrite of what I did in MooTools. This is a JQuery plugin, but if Javascript is disabled, the images still display. Like JAIL, I am using a hidden attribute for the source of the image, but unlike JAIL, I only do this via javascript. There is no need to break your HTML.

/*
* jQuery OnDemand Image Loader
*
* Developed by
* Copyright 2011 - Anthony McLin
* http://anthonymclin.com
* Version 1.0
* Licensed under GPLv2
*/
(function($){
	$.fn.onDemandLoader = function(options) {
		
		// Configuration
		options = $.extend({
			selector: null,
			spinner: null,
			callback : jQuery.noop
		}, options);
		
		//Change the source of all the images to point to the loading image
		$(options.selector).each( function() {
			//Write the original source to a temporary location
			$(this).attr('_src', $(this).attr('src'));
			//Change the image source to the loading image
			$(this).attr('src', options.spinner);
		});
	};
	
	//Load a specific image by changing the source back
	$.fn.onDemandLoader.loadImage = function(img) {
		$(img).attr('src', $(img).attr('_src'));
	};
})(jQuery);

Here's how I used it in conjunction with JQuery Cycle. As you can see, when each slide starts displaying, the next one is triggered to load the image.

/* Author: Anthony McLin
 * Fires the slideshow
 */
$(document).ready(function() {
	//Swap out the images for on-demand loading
	$('.slideshow').onDemandLoader({
    	selector:	'.slideshow img',
    	spinner:	'common/blank.gif'
    });
	
	//Start the slideshow
	$('.slideshow').cycle({
		fx: 		'fade',
		speed:		500,
		timeout:	5500,
		next:		'#next',
		prev:		'#prev',
		before:		function(currSlideElement, nextSlideElement, options, forwardFlag) {
			//Trigger the next slide for loading
    		$('.slideshow').onDemandLoader.loadImage(nextSlideElement);
    	}
	});
});

You'll also notice that I'm providing a loading image or "spinner" that gets displayed until the loading happens. I recommend a 1 pixel square blank gif for this to prevent browsers from displaying the ugly broken image icon.

One big caveat.

I highly recommend defining the size of your images via CSS, otherwise once they load, they'll display at the same size as your spinner did. This can lead to tiny thumbnails where you want a nice big image.

Categories: 

Comments

Do you have a site where I can see a demo of it used with jQuery?

- james (not verified)

james wrote:
Do you have a site where I can see a demo of it used with jQuery?

James, take a look at the site I linked above. While not a demo site, it is using this code.

- Anthony McLin

Anthony, good work but I was just testing the link above in FF4 and it doesn't seem to work (just inspecting with Net tab in Firebug). Am I missing anything?

I am the author of JAIL and I chose to require an HTML change to make it work when JS is disabled as I've noticed that using your approach or Mika Tuupola's one (writing the original source to a temporary location, such as '_src' or 'original') is not fully supported in all browsers (tested in FF3 and FF4).

Looks pretty neat (and exactly like the solution I was looking for), but I've got two questions and would be psyched if you could help me out:

1.) Is it possible to load multiple Images (all images with a specific class) at once? Something like this:

$('.slideshow').onDemandLoader.loadImage($('.theme1'));

2.) Is it possible to trigger the loading when an anchor is clicked (and not automatically within a slideshow)? Something like this:

$('#showTheme1').click(function() {

$('.slideshow').onDemandLoader.loadImage($('.theme1'));

}

would be amazing...

Thank you very much,

Lucas

- lucas (not verified)

Sorry for commenting again, but I've got a new question. The two questions above however can be ignored (because both are solved -yes it is both possible). But there's a new one: When I put the onDemandLoader in my $(document)ready function, and preview the Website locally (from my machine) the image seems to be loaded anyway (cause it appears for a fraction of a second before the blank.gif takes its place).

Is this because I'm loading the page from my local machine (and it'll work online), or are there any complications with other stuff happening in my javascript:

$(function(){$("#preloader").show();})

$(document).ready(function() {

$('.galerie').onDemandLoader({

selector: '.galerie img',

spinner: 'common/blank.gif'

});

$().magicScroll();

});

$(window).load(function(){[...]});

I would be really happy if you could help me. Thanks in advance, best Regards

Lucas

- lucas (not verified)

I'm glad you figured out your first two questions Lucas.

This script doesn't stop the original image from trying to load. That's impossible without preventing the image from displaying in the original DOM structure.

Since you're loading from localhost, it's likely that the image loads before it can be swapped out. It's also very possible that you have the image already in your browser cache. You should certainly test your page from a remote location. I recommend trying it with a large image to ensure a sufficient loading time.

- Anthony McLin

Anthony, i have the same question that Lucas did:

"2.) Is it possible to trigger the loading when an anchor is clicked (and not automatically within a slideshow)? Something like this:

$('#showTheme1').click(function() {

$('.slideshow').onDemandLoader.loadImage($('.theme1'));

}"

but different from him, i couldn't figure it out! hehe...

could you help me please?

(and sorry about my english... hehe)

- Endo (not verified)

Endo, what you have should work. You just need to first make sure to instanciate this plugin against your images:

$('.slideshow').onDemandLoader({

selector: '.theme1 img',

spinner: 'common/blank.gif'

});

Also, you probably want to make sure that you're pointing to the img element, and not a container:

$('#showTheme1').click(function() {

$('.slideshow').onDemandLoader.loadImage($('.theme1 img'));

}"

Since I made this script specifically to load a single image at a time, you may want to try to make it work for a single image before trying to do multiple.

- Anthony McLin

How can I use the funktion with the cycle Plugin when I have the images in tags (the are the cycle elements?

some text…

some text…

some text…

Can you help me? Thanks!

- Marc (not verified)

Hello,

Is there a way to implement this plugin using cycle with thumbs that calls the bigger images?

thanks in advance

regards

- luz (not verified)

This is a pretty great bit of code.

I have a question I'm hoping you can help with, I can't seem to grasp where I should add the appropriate functions (likely just hide() and fadeIn() functions) so that the image doesn't appear until it's fully loaded. Any ideas?

- Dave (not verified)

luz

Y

luz wrote:
Hello,

Is there a way to implement this plugin using cycle with thumbs that calls the bigger images?

Yes, take a look at JQuery cycle's example pages. They show how you would handle thumbnails or links to trigger image display.

- Anthony McLin

Dave wrote:
I can't seem to grasp where I should add the appropriate functions (likely just hide() and fadeIn() functions) so that the image doesn't appear until it's fully loaded. Any ideas?

That's tricky. You'd need a way to continuously check an image to see if an image is fully loaded or not. Then you could call my plugin or fadeIn(). I've looked for ways to do what you are trying and I really don't recommend it. Instead, I'd just put an animated loading image behind the image you plan to display. That way it looks like there is a loading effect happening.

- Anthony McLin

I did set an animated image for the background of the container and it works fine for me. Using the large images that I'm using, it looks like a vertical wipe.

However, I'm building it for a client and they said they didn't like that particular transition (fooled them at least, but now the jokes on me apparently) so I figured if I could figure out how to wait to display the image until it was loaded, I could apply a few different transitions from there. Trouble is I can't quite see how to get from here to there.

I'll keep thinkin' on it. If something pops into your head, give me a shout. And thanks again for posting your code online, has definitely saved me a few headaches on this project.

- Dave (not verified)

I was able to use this code to almost get it to work (I'm also running this through the cycle plugin):

$.fn.onDemandLoader.loadImage = function(img) {

$(img).hide().attr('src', $(img).attr('_src') + '.jpg').load(function(){

$(this).fadeIn(1000);

});

};

Only thing I can't seem to figure out, is that it only works on the first image in the series, but not subsequent images as you navigate through...

- Dave (not verified)

Dave wrote:

Only thing I can't seem to figure out, is that it only works on the first image in the series, but not subsequent images as you navigate through...

That's because you aren't really waiting for an image to finish loading. What that code does is:

1) Hide the image regardless of whether it's actually loaded or not

2) Immediately fade the image back in over a 1 second interval.

Really I think the best way to do what you want is with an image preloading script.

- Anthony McLin

Thanks Anthony for your answer.

my gallery is working with cycle thumbs, but when I added your code it stopped loading the big images. Site loads very quick but when I select and click on a thumb it doesn't load anything...

- luz (not verified)

luz wrote:

my gallery is working with cycle thumbs, but when I added your code it stopped loading the big images. Site loads very quick but when I select and click on a thumb it doesn't load anything...

I haven't tested that situation, but my example should work fine in that scenario... you're probably getting an error that you should look into.

- Anthony McLin

Nice solution to a common problem, I just found your site through Google - I thought there would be more jQuery slideshows with Ajax image loading. It seems like they should go together. Giving your plugin a try now! Thanks!

Hi,

OK, I need to ask a silly question. When you say "high resolution" do you mean 300dpi? I thought the browsers could only display 72dpi...

Trish wrote:
Hi,

OK, I need to ask a silly question. When you say "high resolution" do you mean 300dpi? I thought the browsers could only display 72dpi...

Trish you're absolutely correct about browsers only displaying 72dpi, and the confusion is probably because I made a poor choice of words. I probably should have said "large dimension" or "large file size" instead of "high-resolution".

Now to be fair, if you upload a 300 dpi image to a website, browsers will display the full image, just scaled up to 72 dpi. Browsers don't care what the "print size" (inches) dimensions are for an image, they only care about the total pixel size (inches x dpi).

So a 4" image at 300dpi is 1200px wide, which is exactly the same as a 16.66" wide image at 72dpi. As long as the image is 1200px, the browser will display it at 1200px wide, regardless of whether it was saved as 300dpi or 72dpi.

- Anthony McLin

I like it. I put it together with my image preLoader.

Any clue to use this code for multiple images at once?

Multimage loader was easy. This is the code:

j.fn.onDemandLoader.loadImage = function(imgSet) {

j(imgSet).each(function(){

j(this).attr('src', j(this).attr('_src'));

})

};

I also changed the target selector to make it explicitly the parent of the selector:

this.find(options.selector).each( function() {...

}

Thanks for the plugin.

thanks for this , i was having trouble loading a jquery cycle of 85 images plus a mp3 who must play onload, with this the mp3 plays pretty well while the images load all this with no cache.

- sin_family (not verified)

Cheers for this,

I slightly modified it to work using background images on elements instead of straight image elements.

Works great, thanks for the plugin

- Simon W (not verified)

Great plugin - i have one problem though, which i sure is simple enough to solve but im quite new to all of this.

How do i target the img if its within a wrapper for example -

?

i tried changing this to

$('#slideshow div img').onDemandLoader({

selector: '.slideshow img',

spinner: 'common/blank.gif'

});

but no luck :(

Anyone?

- sami (not verified)

great job.

the architectural site loads super fast considering the number of images in the cycler.

Hello,

I want to have to do slideshow with iframe instead of image, so on-demand solution you have sounds good for the heavy contents I will be displaying. Do you think your solution will work with iframe? thanks!

- Jeff D (not verified)

Probably would work with iframes, but you'd need to tweak the source a bit to make it work.

- Anthony McLin

Add new comment