Introducing LazyLoad 2.0
While improvements in browsers means more cool APIs for us to play with, it also means we need to maintain existing code. With Firefox 4's release came news that my MooTools LazyLoad plugin was not intercepting image loading -- the images were loading regardless of the plugin, much like it always had with WebKit-based browsers. Intercepting had continued to work within Internet Explorer but IE's reign of dominance is dying. Clearly I needed to bite the bullet and code LazyLoad to use custom data- attributes to store the real image path.
Note: It was always apparent that using custom attributes would become a necessity, but I hated doing so. The big issue is with this is that it completely bricks image viewing for browsers which don't support JavaScript. Since popular demand is calling for the data- attributes, and browsers are moving toward loading images before JS can intercept them, this was the really the only option.
Enter LazyLoad 2.0. This second generation of MooTools LazyLoad does introduce breaking changes but the class itself is more compact and dynamic. Here's the new class:
var LazyLoad = new Class({ Implements: [Options,Events], /* additional options */ options: { range: 200, elements: "img", container: window, mode: "vertical", realSrcAttribute: "data-src", useFade: true }, /* initialize */ initialize: function(options) { // Set the class options this.setOptions(options); // Elementize items passed in this.container = document.id(this.options.container); this.elements = document.id(this.container == window ? document.body : this.container).getElements(this.options.elements); // Set a variable for the "highest" value this has been this.largestPosition = 0; // Figure out which axis to check out var axis = (this.options.mode == "vertical" ? "y": "x"); // Calculate the offset var offset = (this.container != window && this.container != document.body ? this.container : ""); // Find elements remember and hold on to this.elements = this.elements.filter(function(el) { // Make opacity 0 if fadeIn should be done if(this.options.useFade) el.setStyle("opacity",0); // Get the image position var elPos = el.getPosition(offset)[axis]; // If the element position is within range, load it if(elPos < this.container.getSize()[axis] + this.options.range) { this.loadImage(el); return false; } return true; },this); // Create the action function that will run on each scroll until all images are loaded var action = function(e) { // Get the current position var cpos = this.container.getScroll()[axis]; // If the current position is higher than the last highest if(cpos > this.largestPosition) { // Filter elements again this.elements = this.elements.filter(function(el) { // If the element is within range... if((cpos + this.options.range + this.container.getSize()[axis]) >= el.getPosition(offset)[axis]) { // Load the image! this.loadImage(el); return false; } return true; },this); // Update the "highest" position this.largestPosition = cpos; } // relay the class" scroll event this.fireEvent("scroll"); // If there are no elements left, remove the action event and fire complete if(!this.elements.length) { this.container.removeEvent("scroll",action); this.fireEvent("complete"); } }.bind(this); // Add scroll listener this.container.addEvent("scroll",action); }, loadImage: function(image) { // Set load event for fadeIn if(this.options.useFade) { image.addEvent("load",function(){ image.fade(1); }); } // Set the SRC image.set("src",image.get(this.options.realSrcAttribute)); // Fire the image load event this.fireEvent("load",[image]); } });
Using LazyLoad is as simple as:
<script> /* do it! */ window.addEvent("domready",function() { var lazyloader = new LazyLoad(); }); </script> <!-- then in the body --> <!-- in-page image format --> <img src="/images/blank.gif" data-src="/images/102_1139.jpg" /> <img data-src="/images/102_1139.jpg" />
Changes to version 2.0 of LazyLoad include:
- You may now fade images in upon load
- Uses a customizable data- attribute to store the real image src.
- The resetDimensions options is no longer available
- Document size is calculated during scroll, as dynamic pages change in size frequently.
- Class code size is much smaller.
Lazy-loading images will never go out of style, as it saves your server bandwidth and saves your users from load images that they never scroll to. Adding a fade effect allows for images to be gracefully placed within the page. Let me know if you have further ideas for LazyLoad, as it should only get better!
does your img syntax is best for SEO?
y dont keep img src= syntax and add class LazyLoad for working
Because if the SRC is set initially, Firefox, Chrome, and Safari will load the image regardless.
data-src
bad idea…Is there a different attribute you’d recommend?
Well Google Images uses
data-src
to their lazy loaded script too! so is not a bad idea ;DThe data-src problem for me in using this script in CMS with WYSIWYG they don’t use it. So i don’t need it any more with manual use. The second problem that old version work with error in opera, load page and hide image before scroll…
Again though, the “old” method will no longer work in Chrome, Safari, or Firefox.
R.I.P. LazyLoad :(
I hope you’re referencing the technique and not my class. If you want to keep the “old method”, you can simply change the realSrcAttribute to “src”.
realSrcAttribute=”src” useless and does nothing :)
Hey David,
The plugin is awesome and it was really easy to get it to work.
I don’t think that the data-src is a bad idea! It will work in most recent browsers anyway! And SEO??? You still have the title and alt tags, don’t you?
A quick question – don’t you think that it will be better if you use the container to get the elements in it and to watch it for scroll? Right now you always get all images in the DOM, but I don’t see the real sense into using a container, and then still getting all images in the dom.
I made this small change to get it working the way I wanted :)
Kind regards,
Daniel
Excellent point! I’ll update that tonight.
For SEO and for those who don’t have Javascript enabled, don’t you think, it would be better to keep the real image src in the HTML and replace it by the blank image during the initialization of the lazyload (and of course, storing the real src in an array).
That’s the problem — you can no longer rely on that technique. Safari and Chrome never respected it, and Firefox 4 stopped doing so. That technique simply isn’t viable anymore.
You can just use a simple additional Element which includes the normal img-Element
Sorry, the *noscript* has been deleted
Hi, thanks for the new version of this script. Works fine for me, but in FF4 if i use window i got the following error:
Error: this.container.getElements is not a function
Source: LazyLoad.js
Line: 40
If i switch to document it works fine.
Thanks and Greetings from austria
Leo
I made a big update to help that logic be more stable. Go ahead and update!
Hi, the update works fine. Thanks for the Script. I have build an extension for the CMS Contao based on your script. I hope thats okay with you. It’s LGPL and your copyright is in :)
Hi,
i think your script is so great that i developed an extension for the CMS Contao with contains your javascript. I hope this is okay for you. It’s LGPL and your Name and Copyright are untouched. You can find this here: http://www.contao.org/erweiterungsliste/view/lazy_load.html
This works with every WISYWIG code and i think the people will love it.
Thanks for your Work
greetings Leo
Hi, I’m trying to test lazyload so I can use it on my project but it doesn’t seem to be working properly. I’ve uploaded the test here but it just loads everything at once… http://dsgn.gs/lazyload/
Cheers
Try adding the height and width attributes to your images.
Thanks for the code, David. I appreciate it. I have a question though. That’s a pretty long chuck on javascript to put directly into my HTML. I made a new external jst file to try and clean up my code. It looks like this:
I’m not too experienced with javascript. Can you tell me what I’m doing wrong? Do I need that big chunk of code on every page? By the way, the more you can strip the demo code down the better. A lot of developers put extra styling and scripting in their html demos and it just makes for more digging that can be frustrating. That’s just me though.
Thanks for sharing this with us,
Sam
Looks like I put some code in there that didn’t agree well with that comment. Basically I just took the code, put it in a js file, then did <script src= and the javascript file so everything was external.
Have these changes been implemented in your WordPress plugin?
http://wordpress.org/extend/plugins/mootools-image-lazy-loading/
I have not… I didn’t even know that plugin existed.
Really?
Would you be interested in forking it, or should I go ahead and do it myself?
Go for it; I’m super busy.
Hi, I’ve got the script up and working, but my problem is that images that I don’t put data-src on to show
visibility:hidden
on them. The only way I can get them to show is but addingdata-src
.Is there not a way to have some images load with lazy load and some just load like a regular image on the same page?
ie:
image1.jpg will not load at all, it only shows for a split second and then disappears. It just shows hidden and image2.jpg will load fine with lazy load.
Thank you!
Hi David,
great script!
I’ve just released a plugin for Joomla! with your script.
http://joomla-extensions.kubik-rubik.de/jll-joomla-lazy-load
Thank you very much for your work!
Regards
Viktor
Sorry, the right link is: http://joomla-extensions.kubik-rubik.de/llfj-lazy-load-for-joomla
Thank you!
Viktor
Hi David,
We started using your script and it’s working quite well, we used it to load distant images from various webservers, it’s quite efficient. I just added a small patch around line 56 :
var elPos = el.getPosition(offset)[axis];
becomes
var elPos = el.getPosition(offset)[axis] - this.container.getScroll()[axis];
LazyLoad would then detect images within the range when pages are loaded with an offset (when reloading an already scrolled page for example).
Greetings
Julien
about the internal loadImage function, I guess it will be better like this.
save memory and make html code clean, most of time, the
realSrcAttribute
and load event of image are used only once. by the way, nice job, David.Thanks for sharing this class! I added a bit of code to only target those images containing the
data-src
attribute. What do you think?I’m trying to get this working inside a scrolling div rather than the entire window, but cant seem to get the container option to correctly set as the div class.
Is it possible to do this with this plugin? I know the jquery version of lazyload has implemented this feature.
Nevermind, cracked it :D
Hello Coreyrs, please tell us how you did that.
Thanks in advance.
I tried to change the default container to a div… but unsuccessfully. I had to hack the Class editing the following code:
Add an option:
Edit the line from
…
to
…
…
I have an iPad application running as a webview. Damn developers who built it created a single webpage that on swipe shows and hides div elements to emulate page flips. Problem? There are over 300 images and crashing my third gen iPad. Darn retina. Do the hidden div elements load with lazy load automatically because they are technically there or do they not load until made active?
Also is there the concept of lazy unload? Obviously when they get thru the entire app all images will be loaded and crash from having no memory left on the device. Nice if when a div hides you coulda load the image to free up the memory.
Any advice/examples would be appreciated.
JR
Hi,
I have the same problem. Try use this project to unload viewed pictures.
http://www.appelsiini.net/projects/viewport
One other item. What if most of my images are loaded thru CSS? Out of luck?
Thanks.
JR
Hey, For some reason, the script just loads all images at once, instead of loading upon scrolling into view. I gave each image a height and width and still
Nevermind, I fixed it. I had to add style=”display: block; visibility: hidden; opacity: 0;” to each image… Just like each image has on your live demo page…
Your demo doesn’t work anymore!
Fixed! :)
You can just use a simple additional noscript Element which includes the normal img-Element
In Chrome it doesn’t work for me.
At time of execution the LazyLoad script gets a Position of 40 for all images in my gallery-container. The img-Elements all have width and height set as attributes.
This results in Chrome loading *all* images when loading the page.
Is this issue known?
WebKit loads all images if the SRC attribute is set regardless of any plugin, so make sure true SRC’s aren’t set. My demo works in Chrome so maybe it’s a MooTools version issue?
Excellent work when comparing other’s work. And if you do this plugin for jquery it’ll really helpful. Can you do this lazyload plugin for jquery library?
Thanks in advance.
Hello David, i want to ask. how to handle if image not found.
Thanks
hello the lazyload plugin for joomla is not w3c validated.
the data-src attribute is not valid.
lazyloadforjoomla / blank.gif “data-src =” http://www.xxxxxx.it/images/banners/image ….
How do you solve this problem?
Can you post the right code?
Hello,
i have a script php for load image from folder because i have many image…., how to use lazyload for give effect to my gallery?
Thanks.
I fucking hate this bullshit lazy image wank. It seems to be infesting loads of sites, and like you say using lazy shite excludes those without JS. I surf without JS (if you have to ask why, you will not understand the answer) and I have noticed quite a few sites where they use this lazy crap to exclude content from those that are not willing to engage with the site’s JS abuses (mostly privacy attacks). IIRC Buzzfeed.com only uses this on “content”, the ads and junk that are not actual article content get served traditionally.
I am currently working around the issue with Privoxy, making it edit shit web pages to put the “data” in the src field.
FILTER: trans Bullshit image hiding
The trans filter is then just applied to domains need it.
> Lazy-loading images will never go out of style, as it saves your server bandwidth
> and saves your users from load images that they never scroll to.
I couldn’t care less about server bandwidth, don’t fill pages up with huge images and other media guff if bandwidth is a concern!
And here you are making the user experience worse for the user. The above has an assumption something along the lines that the user is connected to the web server via gigabit ethernet, and that images do not take time to load. If a web pages looks to have loaded it is awful for it to start loading again when scrolling down, especially considering that if there isn’t a gigabit+ link the user might end up looking at gaps on web pages, or stuff pops-up or otherwise jumps about.
> Adding a fade effect allows for images to be gracefully placed within the page.
Ewwww, that kind of shit just makes things even worse by making things feel slow.
> Let me know if you have further ideas for LazyLoad, as it should only get better!
dd if=/dev/zero of=web2.0
Hello Ranty Raterson, So, let me understand, you want to you the web but one where everything is on your terms – good luck with that.
Hi David Walsh!
when I use the ajax lazyload not work, please help me
Code here:
Hi David!
Excellent work! Thanks for sharing.
In one of my recent projects (a joomla component to be precise) for a my customers, we are having problems loading the images on one of the pages. The site crashes while trying to load the images.
The problem is basically due to memory leaks and bandwidth consumption. Literally, the the page is too long (filled with images) but the customer prefer to leave it that way. I decided to give your plugin a shot. Soon, I realized that the images on the default page are been linked as background images. As a result, the effect of lazy load is disregarded by those images.
Can you please advice me on how to resolve this problem. The site I am referring to is: http://www.bazoolo.at. A right click on the default page to view the source code will reveal more of the problems I am talking about. By the way, we are experiencing problems only with iPhone on this site.
Hi David!
I am having problems with one of my recent projects. iPhone find it very hard to load the default page on http://www.bazoolo.at. This is due to memory leak and the page is way to long and the customer prefer to keep page that way.
I decided to give your plugin (lazyload) a shot. Still, the default page would crash on iPhone. I found out that because the images on the page are loaded on the background of the page, lazyload is unable have any effect on the behavior of the images.
Can anyone here please help me resolve this issue? I’ll be very grateful for your help. Please right click on the default page of the URL above to view the source code. Thank you!
Hi
I am wondering if this works with dynamic contents? Because i tried loading it, and it gives me a referenceerror LazyLoad is not defined.
Hi,seems there is some bug,when src and data-src are equal load event not called so here is fix for it in loadImage function :
Hi David!
Excellent work! Thanks for sharing.
In one of my recent projects (a joomla component to be precise) for a my customers, we are having problems loading the images on one of the pages. The site crashes while trying to load the images.
The problem is basically due to memory leaks and bandwidth consumption. Literally, the the page is too long (filled with images) but the customer prefer to leave it that way. I decided to give your plugin a shot. Soon, I realized that the images on the default page are been linked as background images. As a result, the effect of lazy load is disregarded by those images.
I have a question, I have a bootstrap carousel set, am I able to implement this lazyload to it?
If so, how ?
Thanks, It’s kinda my first web after some time…