Load a low-res background image first, then a high-res one

A bit late, but you can use this extremely simple solution: You can put the two images in the css background:

  background-image: url("high-res.jpg"),url("low-res.jpg");

The browser will display the low-res image fist, then display the high-res over the low-res when it has been loaded.


Let's try a basic one :

<img border="0" 
     style="background:url(http://i.stack.imgur.com/zWfJ5.jpg) no-repeat; 
            width:1920px;
            height:1200px"
     src="http://i.stack.imgur.com/XOYra.jpg" width="1920" height="1200" />

zWfJ5.jpg is the low-resolution version, and XOYra.jpg is the high-resolution version.

If there is a way to arrange the loading so the background-image displays first, this could be the simplest i can think of.

where low resolution 44k: low resolution 44k:

and high resolution is 1.16M high resolution is 1.16M

result :

jsFiddled here ( this needs a bigger image for loading comparison. )


Here's the method I use...

CSS:

#div_whatever {
   position: whatever;
   background-repeat: no-repeat;
   background-position: whatever whatever; 
   background-image: url(dir/image.jpg);
   /* image.jpg is a low-resolution at 30% quality. */
}

#img_highQuality {
    display: none;
}

HTML:

<img id="img_highQuality" src="dir/image.png">
<!-- img.png is a full-resolution image. -->

<div id="div_whatever"></div>

JQUERY:

$("#img_highQuality").off().on("load", function() {
    $("#div_whatever").css({
        "background-image" : "url(dir/image.png)"
    });
});
// Side note: I usually define CSS arrays because
// I inevitably want to go back and add another 
// property at some point.

What happens:

  1. A low-res version of the background quickly loads.
  2. Meanwhile, the higher resolution version is loading as a hidden image.
  3. When the high-res image is loaded, jQuery swaps the div's low-res image with the high-res version.

PURE JS VERSION

This example would be efficient for changing one to many elements.

CSS:

.hidden {
   display: none;
}

#div_whatever {
   position: whatever;
   background-repeat: no-repeat;
   background-position: whatever whatever; 
   background-image: url(dir/image.jpg);
   /* image.jpg is a low-resolution at 30% quality. */
}

HTML:

<div id="div_whatever"></div>
<img id="img_whatever" class="hidden" src="dir/image.png" onload="upgradeImage(this);">

JAVASCRIPT:

function upgradeImage(object) {
    var id = object.id;
    var target = "div_" + id.substring(4);

    document.getElementById(target).style.backgroundImage = "url(" + object.src + ")";
}

UPDATE / ENHANCEMENT (1/31/2017)

This enhancement is inspired by gdbj's excellent point that my solution results in the image path being specified in three locations. Although I didn't use gdbj's addClass() technique, the following jQuery code is modified to extract the image path (rather than it being hardwired into the jQuery code). More importantly, this version allows for multiple low-res to high-res image substitutions.

CSS

.img_highres {
  display: none;
}

#div_whatever1 {
  width: 100px;
  height: 100px;
  background-repeat: no-repeat;
  background-position: center center;
  background-image: url(PATH_TO_LOW_RES_PHOTO_1);
}

#div_whatever2 {
  width: 200px;
  height: 200px;
  background-repeat: no-repeat;
  background-position: center center;
  background-image: url(PATH_TO_LOW_RES_PHOTO_2);
}

HTML

<div id="div_whatever1"></div>
<img id="img_whatever1" class="img_highres" src="PATH_TO_HIGH_RES_PHOTO_1">

<div id="div_whatever2"></div>
<img id="img_whatever2" class="img_highres" src="PATH_TO_HIGH_RES_PHOTO_2">

JQUERY

  $(function() {
      $(".img_highres").off().on("load", function() {
         var id = $(this).attr("id");
         var highres = $(this).attr("src").toString();
         var target = "#div_" + id.substring(4);
         $(target).css("background-image", "url(" + highres + ")");
      });
   });

What's happens:

  1. Low res images are loaded for each of the divs based on their CSS background-image settings. (Note that the CSS also sets the div to the intended dimensions.)
  2. Meanwhile, the higher resolution photos are being loaded as hidden images (all sharing a class name of img_highres).
  3. A jQuery function is triggered each time an img_highres photo completes loading.
  4. The jQuery function reads the image src path, and changes the background image of the corresponding div. In the example above, the naming convention is "div_[name]" for the visible divs and "img_[same name]" for the high res images loaded in the background.

I would normally optimise the image using Grunt or an online tool such as Tiny PNG to reduce the file size.

Then you could choose to defer the loading of the images, I found the following article helpful when it came to deferring images - https://www.feedthebot.com/pagespeed/defer-images.html

The article discusses using a base64 image for the initial loading and then deferring the loading of the high-quality image. The image mark up mentioned in the article is as follows...

<img src="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" data-src="your-image-here">

The JavaScript mentioned in the article is as follows...

window.addEventListener("load", () => {
  const images = document.querySelectorAll("img");
  for (let img of images)
    if (img.hasAttribute("data-src"))
      img.src = imgDefer[i].getAttribute("data-src");
});

I hope this helps.


On Ubuntu / Chrome 71, Milche's answer does not work consistently for me and the higher resolution image (via img src) often loads and resolves before the lower resolution image (via css background) even begins downloading.

My solution is to start with the lower res image as the src and use the Image class to create an unattached <img> instance with the high res image. Once it loads, then update the existing <img> source with the high res image source.

HTML:

<img id='my-image' src='low-res.png' alt='Title' width='1920px' height='1200px'>

JavaScript:

window.addEventListener('load', function() {
    loadHighResImage(document.getElementById('my-image'), 'high-res.png')
})

function loadHighResImage(elem, highResUrl) {
    let image = new Image()
    image.addEventListener('load', () => elem.src = highResUrl)
    image.src = highResUrl
}

Fiddle: https://jsfiddle.net/25aqmd67/

This approach works for lower res images that are simply scaled down as well.