Image requests in hidden content

January 1st, 2024. Tagged: images, performance

You know the pattern: spit out some markup, probably server-side, but hide it for later. On-demand features (not to overwhelm the UI), dialogs waiting to pop, and so on.

<div class="modal hidden">content here...<div>

And what happens when the "content here..." includes resources, such as images? Is the browser going to download them? Let's check.

What to test

  • image in a div hidden with visibility: hidden. The theory is that this is more likely to be downloaded because invisibility still takes a section of the page, the browser needs to calculate geometry of the content, so an image (exp one that has no width/height) will be required to load
  • image in a div hidden with display: none
  • image in a non-expanded <details> HTML element
  • same three things above but with loading="lazy" on the contained images
  • all of the 6 scenarios above but below the fold

Test page

/files/display/test.html, you can see the full source and play with show/hiding and scrolling. At the top of the pages all downloaded images are being listed (thanks to their respective load events)

Results

X-browser!

I tested Firefox, Safari and Chrome and they all behave exactly the same.

More details

  1. All non-lazy images are loaded
    • no matter if they are above or below the fold
    • no matter how their containers are hidden
  2. A lazy image above the fold inside a visibility: hidden loads too
  3. Lazy images inside a display: none or <details> do not load
  4. Scrolling all the way down loads another image, the lazy one inside visibility: hidden
  5. Expanding all the containers (while above the fold) loads the lazy images above the fold
  6. Finally expanding all the containers (and scrolling all the way down) loads the lazy images below the fold

Discussion

So what does all that mean? Assuming we prefer to not load images in hidden content...

  • visibility: hidden is the worst, as expected, avoid
  • display: none and <details> (both behave the same) are slightly better if you use loading=lazy on the images, prefer. And so...
  • Always use loading=lazy on images in hidden content
  • For a foolproof non-loading, use the old technique of commented-out HTML, see below

The old commented out technique

This is a technique that was somewhat preferred in the recent past when we worried about low-powered devices that take a moment to parse large chunks of HTML, when such HTML is not initially required. Since we always need to worry about low-powered devices, it makes sense to refresh on this technique again.

Say this is the hidden content you want to unhide when appropriate:

<h3>Stuff</h3>
<p>
  <img src="https://slowfil.es/file?type=png">
  Integer luctus metus eros, ...
</p>
  1. Step 0: make sure there are no HTML comments inside the hidden content
  2. Step 1: wrap in HTML comments and put in a container:
    <div id="commented">
    <!--
    <h3>Stuff</h3>
    <p>
      <img src="https://slowfil.es/file?type=png">
      Integer luctus metus eros, ...
    </p>
    -->
    </div>
    
  3. Step 3: unhide in an opportune moment:
    const trimmed = commented.innerHTML.trim();
    commented.innerHTML = 
      trimmed.substring(4, trimmed.length - 3);
    /* alternatively...
    commented.innerHTML = 
      commented.innerHTML.replace('<!--', '').replace('-->', '');
    */
    

Happy new 2024!

Feb 9 update

Added experimentation with content-visibility: hidden. It's only supported in Chromes and also didn't help, behaved just like display: none. Sad trombone.

Tell your friends about this post on Facebook and Twitter

Sorry, comments disabled and hidden due to excessive spam.

Meanwhile, hit me up on twitter @stoyanstefanov