treehouse : what would you like to learn today?
Web Design Web Development iOS Development

Lazy Loading Images

Last updated on:

Use a blank.gif as the src of images, and include the width and height of the final image.

<img src="blank.gif" class="lazy" data-src="http://cdn.css-tricks.com/images/full-size.jpg" width="240" height="152"></a>​
/* lazyload.js (c) Lorenzo Giuliani
 * MIT License (http://www.opensource.org/licenses/mit-license.html)
 *
 * expects a list of:  
 * `<img src="blank.gif" data-src="my_image.png" width="600" height="400" class="lazy">`
 */

!function(window){
  var $q = function(q, res){
        if (document.querySelectorAll) {
          res = document.querySelectorAll(q);
        } else {
          var d=document
            , a=d.styleSheets[0] || d.createStyleSheet();
          a.addRule(q,'f:b');
          for(var l=d.all,b=0,c=[],f=l.length;b<f;b++)
            l[b].currentStyle.f && c.push(l[b]);

          a.removeRule(0);
          res = c;
        }
        return res;
      }
    , addEventListener = function(evt, fn){
        window.addEventListener
          ? this.addEventListener(evt, fn, false)
          : (window.attachEvent)
            ? this.attachEvent('on' + evt, fn)
            : this['on' + evt] = fn;
      }
    , _has = function(obj, key) {
        return Object.prototype.hasOwnProperty.call(obj, key);
      }
    ;

  function loadImage (el, fn) {
    var img = new Image()
      , src = el.getAttribute('data-src');
    img.onload = function() {
      if (!! el.parent)
        el.parent.replaceChild(img, el)
      else
        el.src = src;

      fn? fn() : null;
    }
    img.src = src;
  }

  function elementInViewport(el) {
    var rect = el.getBoundingClientRect()

    return (
       rect.top    >= 0
    && rect.left   >= 0
    && rect.top <= (window.innerHeight || document.documentElement.clientHeight)
    )
  }

    var images = new Array()
      , query = $q('img.lazy')
      , processScroll = function(){
          for (var i = 0; i < images.length; i++) {
            if (elementInViewport(images[i])) {
              loadImage(images[i], function () {
                images.splice(i, i);
              });
            }
          };
        }
      ;
    // Array.prototype.slice.call is not callable under our lovely IE8 
    for (var i = 0; i < query.length; i++) {
      images.push(query[i]);
    };

    processScroll();
    addEventListener('scroll',processScroll);

}(this);​

Reference URL

View Comments

Comments

  1. Robin
    Permalink to comment#

    You got a demo?

  2. Robin
    Permalink to comment#

    My bad. Its not working on iPad, is it? Nothings happening on my iPad2.

  3. Permalink to comment#

    Chris, what happens to no-js users? It doesn’t sound like a good idea to do this… as you can see on iPad etc ;)

    • Robin
      Permalink to comment#

      @Anselm: iPad is not a no-js user. It’s a touch/mobile user (different scroll event perhaps?).

      I think we’ve long-passed the station where we should build fallbacks for non-js (standard) users. Especially regarding images.

    • Lorenzo Giuliani
      Permalink to comment#

      you can use a simple fallback in the markup:

      
      <noscript>
      <img src="my_image.png" width="600" height="400" class="lazy">
      </noscript>
      <img src="blank.gif" data-src="my_image.png" width="600" height="400" class="lazy">
      

      and in css using modernizr.js:

      
      .no-js .lazy {
      display:none;
      }
      
  4. I have been building a fast, stand alone & robust lazy loader.

    You’ll find it here : https://github.com/fasterize/lazyload

    Why not join forces ? :)

    There’s a lot of use cases that your lazyloader is not fixing, like onload-inviewport images (so no scroll event). Etc. (see test/)

    Was great reading your code and compare.

    Thank you

    • Lorenzo Giuliani
      Permalink to comment#

      I have seen your code a few days after i wrote this one featured on dailyjs, if I have patches I’ll send you a pull request.

      Here I have an updated Gist: 2171438, can’t remember what changed between the submitted version and the gist.

  5. this is not work full in my blog,,

    please help me please. . .

  6. I’m probably asking a dumb question, but… What would this be useful for? To make images load slower than they should? Why?

    Please, i’m not trying to be harsh, i just don’t get this.

    • Robin
      Permalink to comment#

      It’s to prevent unnecessary image loading and optimize bandwidth usage.

      If a user clicks through your pages, why would you waste bandwidth on that photo at the bottom of your post, when the user never scrolled there to view it?

    • It’s for saving bandwidth. On a high traffic site, say 2 million of 5 million users visit a blog post with a lot of images on it, but never scroll down. Below the fold, there is 750k of images. That’s going to save you a boatload of bandwidth (1.5 million megabytes…)

  7. Jacob
    Permalink to comment#

    I personally like the way this website does it:

    http://www.nomachetejuggling.com/2011/11/11/the-star-wars-saga-suggested-viewing-order/

    (Scroll down quickly.)

    • kayan
      Permalink to comment#

      I like how in that one, the img tags use the real src attributes like normal. It’s a whole lot easier to implement, with no need for special markup or JS fallbacks for each image.

      Unfortunately, this seems to defeat the whole purpose of lazy loading. Looking at a network inspector, I saw every image loaded when the page loads, and loaded a second time when scrolling down. Hiding the images temporarily might have some performance benefits, like in low memory environments such as older iPhones, but it’s not preventing unnecessary network traffic the way Coyier’s solution does.

      It would be great if we could get the best of both worlds – ordinary markup and real lazy image loading – but I have doubts about it being possible. Using real src attributes basically guarantees the images will start loading before JS can intervene.

    • Conor
      Permalink to comment#

      yeah, this one works particularly well.

  8. Permalink to comment#

    On FF11, scrolled down, nothing happened :\

  9. Previous comment didn’t appear (maybe spam folder ¿?)

    Your script have a problem with concurrency. While you are lazy loading the image, and if the place holder is still in the clip rectangle and you are scrolling, you will request again and again the image until the image is totally loaded. You have to move out the callback function to remove image from array the first time you requested it.

  10. Andrew
    Permalink to comment#

    I’m having trouble getting this to work in IE (big surprise) with WordPress and loading in the images via PHP. Any ideas how I could get that to work?

  11. Permalink to comment#

    Hi Chris,

    just wanted to mention that I think there’s a mistake in the code. I think you’re using the splice-Method inside the processScroll function wrong – second Parameter should be ’1′ instead of ‘i’. I had some weird results with splice(i, i) (of course).

    Am I wrong?

    Greetings

    Dominique

  12. Mary
    Permalink to comment#

    Search engines will not be able to find those images, are they?

  13. Permalink to comment#

    no they won’t… I also can’t think of a good way to embed them in another way so SE will see them. You could provide an alternative, standard html page where the images are not lazy loaded for SEs only (catch’em and redirect or simply put a link on the bottom/top of the page).

    • Ian
      Permalink to comment#

      Hey, probably more work than people may like, but one way is to check the user agent to see if it’s any of the popular search engines, then if it is just load the images like you normally would. Although I’m unsure if this will spark a red flag. I imagine there’s a possibility they send alternative user_agents to a site as well and compare content, to detect potential SE manipulation. Just a thought.

  14. Chris: “It’s for saving bandwidth.”

    Doesn’t it also reduce page load time, which can be an SEO factor (if it’s bad enough)? And a usability factor, since it seems like there’s some page functionality that doesn’t work until the DOM is loaded (drives me nuts on our local newspaper site – the page is “frozen” until their dysfunctional server loads all the ads).

  15. Kevin
    Permalink to comment#

    Thanks for the script. Is there a simple way to add an effect? Like a fade in or delay?

  16. Haven’t tried it yet, but imo, a way to gracefully degrade (and probably SEO-friendlier) approach would be to have links to full images.
    You could include some image data (width, height, alt/title) as data- attributes in the link
    Then, via JS, you:

    1) Create the <img /> tag. On the src attribute, you use a blank.gif and on the data-src, you put the URL to full image (that you get from the href attribute on the a element). Optional: for a better user experience, you also set width, height, alt & title, which you get from data- attributes, also on the a element.

    2) You lazy-load them as usual, when user scrolls the page.

    • Permalink to comment#

      the scrapers/spiders/bots don’t usually trigger most js events so none of those attrs would ever fire.

  17. Giuseppe
    Permalink to comment#

    There’s something I think you’ve overlooked.

    If JavaScript is disabled no images will be shown at all, which is no good from a user point of view.

    Some people prefer to browse without JavaScript enabled, and websites should be able to degrade their functionality accordingly.

    In this case, if JavaScript is disabled, it’s better to show all images.
    Even though it cost more in bandwidth terms, but at least the user will be served with a readeable page.

  18. Harlowe Thrombey

    Only Web Developers think Lazy Loading is cool. (The same guys who used to think Ajax was cool… and before that frames… whatever the Design Fad Du Jour is)

    The problem with lazy loading is it prevents quick scanning by the human eye. In fact, Lazy Loading assumes the human eye is incapable of scanning images.

    Google destroyed their Image Search with Lazy Load.

    What do so many Web Developers like to DEVOLVE the ease of browsing?

  19. Harlowe Thrombey

    Yeah and Google thinks their Image Search is fine, too.

    Monkey Engineers

Leave a Comment

Use markdown or basic HTML and be nice.