Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep an image size intrinsic, before it is loaded, so layout is stable?

I had always thought you could specify an <img/>'s width and height attributes so the browser knows its physical dimensions without having to download (at least the header) first. But apparently the attributes are treated as CSS properties - and are thus overridden when you try to do something like width: 50%; height: auto. Then, until the image is downloaded, the computed height remains 0.
Is there a way to explicitly set the image's physical dimensions (effectively its aspect ratio) via HTML/CSS?

(...and avoid "flashes of unknown aspect ratio" / lots of nervous re-layouting when using Masonry - which you could perhaps work around, but I'm more interested in the basic problem. Even if i'm struggling with asking succinctly about it :)

like image 561
kubi Avatar asked May 25 '16 18:05

kubi


2 Answers

So, as soon as we set height: auto by CSS, the browser ignores the intrinsic size set by width and height HTML attributes.

Possible workarounds and a possible solution for the future:

  1. Use the Intrinsic Ratio Box hack
    <div style="height: 0; padding-bottom: 42.857%; position: relative">
      <img style="position: absolute; left: 0; height: 0; width: 100%; height: 100%" … />
    </div>
    
  2. Using data URIs and JavaScript pre-load a placeholder with the same aspect ratio (beware of rounding errors!)
    <img src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQIAJQAlAAD/2wBDAFA3PEY8MlBGQUZaVVBfeMiCeG5uePWvuZHI////////////////////////////////////////////////////wAALCAAPACMBAREA/8QAFgABAQEAAAAAAAAAAAAAAAAAAAEE/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/aAAgBAQAAPwDQgoICv//Z"
      onload="this.src='https://via.placeholder.com/3500x1500.jpg'"/>
    
    If we are using the "blurry placeholder" technique, the framework might already be there.
  3. Same as two, but use an SVG - easier to create on the fly, but no blurry placeholder "for free", obviously
    <img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'
      width='350px' height='150px'%3E%3C/svg%3E"
      onload="this.src='https://via.placeholder.com/3500x1500.jpg'"/>
    
  4. If & once intrinsicsize lands (WICG, github/Chrome) and gets adopted, there will be a Proper Way to do this:
    <img intrinsicsize="350x150" width="…
    

All of these have downsides, of course.
You can try 2 & 3 in this Pen - it would need more work & testing, I'll probably wait for 4, which I consider a fix for something that shouldn't need fixing. Stupid browsers :)

like image 114
kubi Avatar answered Oct 12 '22 20:10

kubi


Updating for 2021, we now have a widely supported standards-compliant way to do this is with the CSS aspect-ratio property.

You can simply use the height and width like so:

.myimage {
  width: 50%;
  height: auto;
  aspect-ratio: 1280 / 720;
}

This will first calculate the width as 50% of the parent element, then, with the height set to auto (it's best to explicitly set it to auto in case something else changes the height for maintainability), it will use the value in aspect-ratio to calculate the height it should reserve.

This does affect the image element even after the image has been loaded though. Meaning if the aspect-ratio property doesn't match the image's actual aspect ratio, the image will be stretched to fit. You can prevent this by prepending the "auto" value.

aspect-ratio: auto 1280 / 720;

With that, it will use the defined aspect ratio before the image is loaded and the actual aspect ratio is known, then fall back to auto once the browser knows the intrinsic aspect ratio.

This is not supported by non-standard browsers like Internet Explorer and Safari, so if you want to make this work in those purely with CSS, you'll need to continue using the first workaround in kubi's answer (as a side note, the 4th option is not and has never been on track to become part of the standard).

However, this question would likely not even get asked if it were today (at least not by the same person) because there is now an HTML solution which is the old, pre-responsive web design HTML solution. Most modern browsers now use the image element's height and width attributes to calculate the aspect ratio, while CSS height and width override those values unless they are set to auto.

Therefore...

<img src='myimage.jpg' class='myimage' width='1280' height='720' />
.myimage {
  width: 50%;
  height: auto;
}

This will now reserve the correct amount of vertical height, even in Safari. As of right now, there are still some browser-specific implementation notes to consider.

In Chromium and Firefox, it is currently implemented using the CSS aspect-ratio property with "auto" prepending the calculated aspect ratio.

aspect-ratio: auto attr(width) / attr(height);

Since Safari doesn't support the aspect-ratio property yet as of my writing this, its implementation has a bit more nuance. As a result, it will not reserve space if there is not a valid image value in the src attribute, meaning if you are using old-fashioned JavaScript-based image lazy loading, you will need to include a fallback image.

In any case, you should include height and width attributes on your image tags as much as possible. They actually do something now.

like image 44
XerBlade Avatar answered Oct 12 '22 19:10

XerBlade