Introducing MooTools LazyLoad

Written by David Walsh on Tuesday, August 18, 2009


David Walsh MooTools LazyLoad

Once concept I’m very fond of is lazy loading. Lazy loading defers the loading of resources (usually images) until they are needed. Why load stuff you never need if you can prevent it, right? I’ve created LazyLoad, a customizable MooTools plugin that allows you to only load images when the user scrolls down near them.

The MooTools Javascript Class

/* lazyload */
var LazyLoad = new Class({
	
	Implements: [Options,Events],
	
	/* additional options */
	options: {
		range: 200,
		image: 'blank.gif',
		resetDimensions: true,
		elements: 'img',
		container: window
	},
	
	/* initialize */
	initialize: function(options) {
		
		/* vars */
		this.setOptions(options);
		this.container = $(this.options.container);
		this.elements = $$(this.options.elements);
		this.containerHeight = this.container.getSize().y;
		this.start = 0;
	
		/* find elements remember and hold on to */
		this.elements = this.elements.filter(function(el) {
			/* reset image src IF the image is below the fold and range */
			if(el.getPosition(this.container).y > this.containerHeight + this.options.range) {
				el.store('oSRC',el.get('src')).set('src',this.options.image);
				if(this.options.resetDimensions) {
					el.store('oWidth',el.get('width')).store('oHeight',el.get('height')).set({'width':'','height':''});
				}
				return true;
			}
		},this);
		
		/* create the action function */
		var action = function() {
			var cpos = this.container.getScroll().y;
			if(cpos > this.start) {
				this.elements = this.elements.filter(function(el) {
					if((this.container.getScroll().y + this.options.range + this.containerHeight) >= el.getPosition(this.container).y) {
						if(el.retrieve('oSRC')) { el.set('src',el.retrieve('oSRC')); }
						if(this.options.resetDimensions) {
							el.set({
								width: el.retrieve('oWidth'),
								height: el.retrieve('oHeight') 
							});
						}
						this.fireEvent('load',[el]);
						return false;
					}
					return true;
				},this);
				this.start = cpos;
			}
			this.fireEvent('scroll');
			/* remove this event IF no elements */
			if(!this.elements.length) {
				this.container.removeEvent('scroll',action);
				this.fireEvent('complete');
			}
		}.bind(this);
		
		/* listen for scroll */
		this.container.addEvent('scroll',action);
	}
});

Options for LazyLoad include:

  • range: (defaults to 200) The amount of space from the container’s bottom position that you want to look for images to load.
  • image: (defaults to “blank.gif”) The image to replace the original image.
  • resetDimensions: (defaults to true) Removes the image’s width and height attributes.
  • elements: (defaults to “img”) Images to consider for lazy loading.
  • container: (defaults to window) The container for which to look for images within.

Events for LazyLoad include:

  • onComplete: Fires when all images have been loaded.
  • onLoad: Fires on each individual image once it has been loaded.
  • onScroll: Fires when the container is scrolled.

The Sample Usage

/* very simple usage */
var lazyloader = new LazyLoad();

/* more customized usage */
var lazyloader = newLazyLoad({
	range: 120,
	image: 'logo.gif',
	resetDimensions: false,
	elements: 'img.lazyme'
});

Enhancements

This class isn’t perfect — it does not defer loading of images above the page’s starting point. If you work on a website with a lot of anchor links and you’d like lazyloading of images above the fold, you’ll need to add a bit to this class. Also note that due to WebKit bug #6656, Safari will continue to load the originally images despite the plugin.

Have any more suggestions? Share them!


Follow via RSS Epic Discussion

Commenter Avatar August 18 / #
rborn says:

I thought you were working on a javascript lazy loading / load on request plugin, but this is nice too :D

Commenter Avatar August 18 / #
Tim says:

That’s great, I was looking for exactly this kind of moo script about a year ago and I dind´t had the time to make it myself.

Commenter Avatar August 18 / #

Hey David how are you, i’m a loyalty reader of your blog. i was watching your recent visit to monterrey mexico, i hope you like it i’m living near to this beautiful city. sii yaaa

Commenter Avatar August 18 / #
Daniel says:

Three words… well, one really: Fan-frigging-tastic!

Thanks for sharing, David.

Commenter Avatar August 18 / #

Thats usefull!!!
Gonna take a deeper look on this for sure. Thanks man

Commenter Avatar August 18 / #
Rolf says:

Why are you using filter on the elements? You already collecting all the correct elements using $$(elements)? Maybe I’m overlook something, it’s late ;)

Anyway, nice class, I will have to compare it to the one I wrote a about a year ago (I can probably improve it here and there).. hence my question about using filter.

Also, I added a threshold option to the class to set a minimum size of the image.. it’s pretty useless to apply a lazyloading to small images (it takes longer to do the JS than to display them)…….typing this now, I remember my own comment in the code that the size (width x height) could be set to 20×20 px even though the image size in bytes is 300kb (i should say dimensions instead of size)… so I dunno, it could be a valid addition, it depends on how the developer uses it (in my case it was for my own project, so I knew my 20×20 px images where also 1kb for example and should not have lazyloading applied to.

David Walsh August 18 / #

@Rolf: Filtering is used to (a) determine which images can be loaded normally because they’re above the treshhold and (b) to remove images that have been scrolled-to so that the script is more efficient. Eventually the script shuts itself off when all images have been loaded. The width/height threshhold is a good idea!

Commenter Avatar August 19 / #
Truthseeker says:

When you load the page and use END to jump to the bottom, the script loads all the images in the middle (atleast in firefox) before loading those that are in the view at the bottom. I wonder, is it possible to change that so it really loads only those in view or near?

Commenter Avatar August 19 / #
Rolf says:

@David, oh jeez.. it was really too late yesterday ;) I saw “filter” and thought you was actually filtering on “images” and I was thinking why, because $$(elements) already grabbed all the right elements..

I’ve always just do an each loop to create the correct collection of whatever you need. you now:


var imagesToProcess = [];
this.elements.each(function(element) {
...
imagesToProcess.include(element);
});

Something like that you know, not using the array.filter method. ..i’ll acquaint myself more with array.filter ;) good tip.

I like the idea to remove the event after processing it. Somehow I didn’t think of that and just stored a cached=true item to the image element (and later checking on that item/value). Ofcourse removing the event is cleaner.. another good tip.

Commenter Avatar August 19 / #
Ben says:

Works nicely, but I haven’t really changed my opinion on this as a concept sorry David… lazy loading absolutely murders the user experience.

I expect that when my browser tells me the page has finished loading, it’s not actually lying to me!

Instead when I scroll down in what I believe is a completely loaded page, I have to wait for more stuff to load in – I haven’t interacted with any form controls or links or anything on the page that would indicate to me as a user that I might be in for some loading time… I just scrolled!

I’ll ask the same question I asked last time you posted on this – if your users don’t want to see all that content, why is it there in the first place?

I believe a better implementation is the “more” link at the bottom of a Twitter homepage – it serves the same purpose, but the user remains in control of loading extra content.

Commenter Avatar August 20 / #
Alex says:

Beautiful, even better than youtube!

Commenter Avatar August 21 / #
Rizwan says:

I really love the stuff you post here….you were my entry to the world of javascript – thanks for all the good work :D

Commenter Avatar August 21 / #
tbela99 says:

not working in google chrome

David Walsh August 21 / #

tbela99: Because Chrome uses WebKit.

Commenter Avatar October 08 / #
Claudio Ferreira says:

Very interesing class David. I’ve been following your good work for some time now, specially with the Mootools community.

About the lazy loading of images, it might be useful to check the imgElement.complete property before trying to lazyload. I mean, if it’s already completed loading (possibly cached) there’s no need to lazy load it.

Anyway, tks for sharing this code.

Commenter Avatar October 28 / #

Well done. The jQuery version exists for a while, like others I was seeking one for the Mootools library, lazy to develop one flexible ;-) So thankssss ! Cheers from Japan.

Commenter Avatar October 28 / #

@Ben: Yes, unfortunately in Web 2.0 there are lots of misconceptions. But if I remembered lazy load concept was created to respond of websites with heavy pages and visitors who don’t read or scroll the whole content. You’re a developer. As the google survey reports, a few people really understand the difference between browser and search engine, that’s why Google recently launched http://www.whatbrowser.org/. So about understanding the meaning of “Page loaded”, I think you can cheat in a safely way with lazyload, and keep working with the members of your team, or take part in evangelizing to spread a user experience which doesn’t feet fashion. There is a lack of education, and for example in web agencies there are need to spread the clients “Web is not shopping, the most important result is how the users will use your website and how they will understand your company or your product” and spread some colleagues “Web 2.0 and user interface are not lightbox, sliders and lazyload” for examples. Keep trying evangelizing in a good way, it takes time, around the scale of years, and introduce or deeply develop what’s “user interface, usability, user experience, and user design experience”, inviting and supporting people to take part in. Good luck :-)

Commenter Avatar November 18 / #

Thank you very much, awesome script; I’ve found the jQuery version first, and I was more than glad to see there was a moo version too (developping a website using mootools right now).

Be Heard!

I want to hear what you have to say! Share your comments and questions below.

Name*:
Email*:
Website:  


© David Walsh 2007-2010. Contact David Walsh. Powered by the remarkable MooTools javascript framework.