Search code examples
jquerylazy-loadingretina-displayprogressive-enhancementdeferred-loading

Web page that loads content first, then beautifies it by deferred-loading hi res image swap-ins?


I have made a site that looks acceptable and strikes a balance between having a reasonable load time and not overly-compressing the JPEG images. One concern is that there is a 100% stretching full-screen background, which while it looks fine on 13-inch display, could spell ugliness on something like a 5K retina display. I can't justify making mobile users suffer by serving them huge images though. What I'm looking for is a "lazy loader" or "deferred loader" that will detect display size and pixel density (retina vs non-retina) and serve the highest quality images possible - but only after all of the site content has been loaded using moderately compressed images and is in a browsable state. If possible I would like for the hi-res swap-in images to do a fade-in transition as they overlay and replace the default-res ones. And I would like for it to be clear to the user that the page has loaded and is ready to interact with, meaning no progress bar indicating that content is not finished downloading.

Goals / Steps

  1. Quickly load the basic page with moderately compressed images. The page can now be interacted with and doesn't look terrible. When search engines spider the page it should be considered fully loaded at this stage, otherwise page rank could suffer.
  2. Check the device display size and pixel density to know what resolution images to swap in.
  3. Serve the images in the appropriate resolution but keep them hidden until they are fully loaded and ready to be faded in.
  4. Fade the hi-res images in. (Fade-in does not need to be synchronized across images, each can fade in over its low-res counterpart as it becomes ready)

Am I asking for a miracle? Should I just give up and stick with mediocre somewhat compressed images? I can't be the only one wanting to do this, so I'm trying to find out what techniques are available.

I have found bits and pieces of answers around the web, but nowhere have I seen a solution that ties it all together. There appears to be no "best practice" for handling this, which is surprising. I know that apple.com serves retina images if you view their site on a retina device, but I have no idea how they do it (Example: store photo). I have found a script that lazy loads retina images (on jsfiddle, so I'm not going to link and clutter this post with code), but it's triggered by scrolling and I don't know how to rework the code to load them after the page content instead. Here's another lazy loader that uses jQuery although I haven't been able to get it to work for me, and again I don't know how I could make it load after the page content, nor do I know how so make the images fade in over existing low-res ones, nor do I know how to incorporate retina display detection.

Is there any hope for someone without much javascript knowledge to be able to pull this off using some existing code, or should I continue my meager existence in Lowres Land?

Edit: I think a more relevant term than "lazy loading" might be "deferred loading". I'm finding that lazy loading usually is in reference to content that renders while scrolling down a page. I have refined my search. Will post any findings here. I am currently reading this, but have not digested it all yet.

Edit2: I think jQuery's deferred and fadeIn together, (with media queries for retina) may hold the answer. I found an example in the jQuery documentation that looks like a variation of what I want. But I am in way over my head as far as tying this all together.


Solution

  • First you should really look into lazySizes. This is a lazyloader, which is build on top of the HTML5 responsive images standard (and all polyfills) and also supports swapping low quality images to higher quality images. (This is called low quality image placeholder pattern or short LQIP).

    Here is an example for static width images:

    <img src="low-quality.jpg" data-srcset="normal.jpg 1x, retina.jpg 2x" class="lazyload" />
    

    And here is one for adaptive width images:

    <img src="low-quality.jpg" data-srcset="low-quality.jpg 300w, image-1 480w, image-2.jpg 600w, image-3.jpg 980w" data-sizes="auto" class="lazyload" />
    

    Some parts about what you want to do are not clear or not so good.

    1. Your LQIP plan: In case you have a lot of images (more than 6 images on a page for example). I would not use LQIP for all images, but only for crucial ones or none, because if you add a lot of images, they still need to be downloaded and the browser can only handle 6 requests at once. This means you will not gain too much performance if you gather your page with a lot of small images. In that case it is much better to not download images at all and only load the images, which are in view as soon as possible.

    2. Images after content: I'm not really sure whether I understand this right. Normal images are content and from a perceived performance perspective images are the most important parts of a page. So what other content are you loading? Or what other content is currently loaded after your images? Do you load your content with AJAX? It is crucial to load visible images as fast as possible. And if you trigger an image download via JS it is already hidden from the preload parser, even if you start it right away, which means it is already deferred in the browser's download queue. Additionally if you have 1-3 images in view and about 10 outside of view, it is already great improvement to download only those 1-3 images.

    All this said, in case you want to further delay loading even visible images you can do so by simple adding the class lazyload after some time or even after the onload event.

    Here an example:

    <img src="low-quality.jpg" data-srcset="low-quality.jpg 300w, image-1 480w, image-2.jpg 600w, image-3.jpg 980w" data-sizes="auto" class="big-bg" />
    

    And here the JS:

    $(window).on('load', function(){
         $('.big-bg').addClass('lazyload');
    });
    

    And here is a full example which also includes a simple transition effect using CSS: http://embed.plnkr.co/9tii4RTjYL2CpiQdXlbn/preview