1

I want a very low quality of my background image to load with the initial paint, then a higher quality version to replace it, based on the browser's width.

Here is my HTML :

<div style="background-image: url('data:image/jpeg;base64,[...]');"></div>

I'm giving the div a base64 background-image that's really low resolution.

And in my CSS :

@media(max-width:  600px) { div { background-image:  url(600.jpg) !important} }
@media(max-width: 1000px) { div { background-image: url(1000.jpg) !important} }
@media(min-width: 1001px) { div { background-image: url(2200.jpg) !important} }

And here I'm overriding the inline rule with the appropriate images for each screen width.

Unfortunately, as soon as the CSS file loads, the browser replaces the placeholder base64 image with an ugly partly-loaded version of the higher-quality image.

How can I wait for the background-image to be fully loaded before swapping-it in ?

Edit : as replied in the comments below, this is not a duplicate as the related answer does not address responsive image sizes.

Brachamul
  • 1,886
  • 2
  • 21
  • 34
  • 1
    Possible duplicate of [Show an alternative image while another image loads](https://stackoverflow.com/questions/35375883/show-an-alternative-image-while-another-image-loads) – caiovisk Jul 12 '18 at 03:08
  • @caiovisk thank you for the link. The difference is that my I'm looking to apply a different image based on resolution, and it does not look like standard lazy loading libraries address this for background-images. Only on standard tags with `srcset`. – Brachamul Jul 12 '18 at 09:55

1 Answers1

1

TLDR:
define the hi-res image to be loaded in your CSS as "invisible"

#hi-res {
    visibility: hidden;
}

preload image at the Head

<link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">

and then wait till the DOM has fully loaded to "display" the image with

$(window).load(function(){
    document.getElementById('hi-res').style.visibility='visible';
});


So what you're running into is not a 'loading' issue, but a 'rendering' issue. The browser is already doing what it thinks you want it to do, that is: swapping one image for another after it's "loaded", or more accurately "found" in the DOM. The issue then is that the browser is coming across (loading) that hi-res image sooner than when it is able to quickly render it. Essentially you want to specifically have the browser wait to load an image at a point when it can render it quickly.

The preload attribute should help address this in that it is essentially a request on the DOM that says: Yo, you're definitely going to need this soon, so grab it before everything else and in full. That being said, it doesn't mean it will render all that quickly once the browser is told to display the image.

So, if you really want to double tap this to ensure the alternative image does not replace the lo-res one before the browser can devote all it's resources to rendering it on screen you can simply have it explicitly hidden from view until everything else is done loading. You can do this by using CSS:

#hi-res {
    visibility: hidden;
}

and then JS on DOM:

$(window).load(function(){
    document.getElementById('hi-res').style.visibility='visible';
});

Preloading
Back in the day you could have used lazy loading or even just a simple JS wait script at the bottom of your page.

However, I think the best solution to your problem would be to simply preload your images using rel="preload" as specified in the MDN Web Docs

This could done by preloading the CSS file itself:

<head>
  <meta charset="utf-8">
  <title>JS and CSS preload example</title>

  <link rel="preload" href="style.css" as="style">
  <link rel="preload" href="main.js" as="script">

  <link rel="stylesheet" href="style.css">
</head>

Source

Or more simply on the media elements themselves:

<head>
  <meta charset="utf-8">
  <title>Responsive preload example</title>

  <link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">
  <link rel="preload" href="bg-image-wide.png" as="image" media="(min-width: 601px)">

  <link rel="stylesheet" href="main.css">
</head>

Source

Alex P
  • 319
  • 1
  • 8
  • 1
    Hi and thanks for the answer. If I do preload, that will not prevent the hi-res image from showing while still loading, right ? My issue is with the fact that the hi-res image should replace the lo-res one only when it's fully loaded. – Brachamul Jul 12 '18 at 09:57
  • I read you, and the answer is yes and no. I'm going to amend my original answer to better address your exact question. That's my bad. – Alex P Jul 12 '18 at 14:29
  • 1
    Great, thanks! I've accepted the answer. I went another way in the end, using a low-quality srcset and another with lazy loading. jQuery's lazy loading plugin waits till the image is loaded before rendering it, so it works well. – Brachamul Jul 12 '18 at 15:51
  • Thanks much and no worries. Lazy loading with jQuery is definitely a legit solution. Think we're always so worried about solving the problem at it's lowest level that we sometimes overlook the solutions others have already built – Alex P Jul 12 '18 at 16:18