30

When I use image tags in html, I try to specify its width and height in the img tag, so that the browser will reserve the space for them even before the images are loaded, so when they finish loading, the page does not reflow (the elements do not move around). For example:

<img width="600" height="400" src="..."/>

The problem is now I want to create a more "responsive" version, where for the "single column case" I'd like to do this:

<img style="max-width: 100%" src="..."/>

but, if I mix this with explicitly specified width and height, like:

<img style="max-width: 100%" width="600" height="400" src="..."/>

and the image is wider than the available space, then the image is resized ignoring the aspect ratio. I understand why this happens (because I "fixed" the height of the image), and I would like to fix this, but I have no idea how.

To summarize: I want to be able to specify max-width: 100%, and also somehow make sure the content is not reflowed when the images are loaded.

benvc
  • 14,448
  • 4
  • 33
  • 54
gabor
  • 1,561
  • 2
  • 12
  • 13
  • 1
    Try [this solution](http://andmag.se/2012/10/responsive-images-how-to-prevent-reflow/)! – Visavì Aug 24 '14 at 15:43
  • 2
    Maybe this post will help you sovlve the problem: https://www.voorhoede.nl/en/blog/say-no-to-image-reflow/ – Dennis Jul 26 '18 at 21:42
  • @Dennis: The linked post describes the same solution as the top answer to this question. – Ry- Jul 27 '18 at 06:59
  • @Ry- ah okay, but maybe the article will explain some more, just for people with same problem and don't understand exactly what happens. but thanks for the info! – Dennis Jul 27 '18 at 07:05

6 Answers6

11

UPDATE 2: (Dec 2019)

Firefox and Chrome now deal with this by default. Simply add the width and height attributes as normal. See this blog post for more details.


UPDATE 1: (July 2018)

I found a much cleverer alternate version of this: http://cssmojo.com/aspect-ratio-using-custom-properties-and-calc/. This still requires a wrapper element and it requires CSS custom properties, but I think it's much more elegant. Codepen example is here (credit to Chris Coyier's original).


ORIGINAL:

From this blog post by Jonathan Hollin: add the image's height and width as part of an inline style. This reserves space for the image, preventing reflow when the image loads, but it's also responsive.

HTML

<figure style="padding-bottom: calc((400/600)*100%)">
  <img src="/images/kitten.jpg" />
</figure>

CSS

figure {
  position: relative;
}

img {
  max-width: 100%;
  position: absolute;
}

The figure can be replaced with a div or any other container of your choice. This solution relies on CSS calc() which has pretty wide browser support.

Working Codepen can be seen here.

wiiiiilllllll
  • 630
  • 6
  • 11
  • If browser support for [css attr()](https://caniuse.com/#feat=css3-attr) improves, we could tidy this up further by adding the width and height as data-attributes instead of using an inline style. – wiiiiilllllll Jul 24 '18 at 10:39
  • +1 for a potential future `attr()` (which wouldn’t require inline styles), but apart from that this is the same thing as before (just more suited to writing manually since you don’t have to calculate `400/600` – but in my case the template can do that). – Ry- Jul 25 '18 at 00:57
  • 2
    Sorry, when you say "this is the same thing as before" what thing are you referring to? I feel like my answer is not a duplicate of any other answer, and it answers the question. Is there any way I can improve it? – wiiiiilllllll Jul 25 '18 at 12:11
  • I mean this is the same strategy as ever with all the same downsides (inline styles). It's a different take, but not a better one without being able to do `attr(height) / attr(width)`. – Ry- Jul 25 '18 at 16:07
  • Added an alternative solution using CSS custom properties. – wiiiiilllllll Jul 26 '18 at 11:01
  • Does the alternative solution reserve the right amount of space? It seems like a box of height `--h` no matter what the width is (or a square when the width is less than the height). – Ry- Jul 26 '18 at 18:01
  • Added an improved alternative solution. – wiiiiilllllll Jul 27 '18 at 15:41
  • "Simply add the width and height attributes as normal." In my tests, this doesn't work... it will screw up the aspect ratio. – Stefan Reich Nov 12 '20 at 09:46
  • @StefanReich That's weird. Here's a pen which shows the effect working correctly in Chrome & Firefox: https://codepen.io/wiiiiilllllll/pen/YzPLKbE – wiiiiilllllll Nov 13 '20 at 15:48
  • @wiiiiilllllll It appears I needed to add "height: auto". Now it works. Thanks for the pen, can I keep it? (Muahaha...) – Stefan Reich Nov 14 '20 at 08:27
8

I'm also looking for the answer to this problem. With max-width, width= and height=, the browser has enough data that it should be able to leave the right amount of space for an image but it just doesn't seem to work that way.

I worked around this with a jQuery solution for now. It requires you to provide the width= and height= for your <img> tags.

CSS:

img { max-width: 100%; height: auto; }

HTML:

<img src="image.png" width="400" height="300" />

jQuery:

$('img').each(function() { 
    var aspect_ratio = $(this).attr('height') / $(this).attr('width') * 100;
    $(this).wrap('<div style="padding-bottom: ' + aspect_ratio + '%">');
});

This automatically applies the technique seen on: http://andmag.se/2012/10/responsive-images-how-to-prevent-reflow/

Paul Pritchard
  • 109
  • 1
  • 4
2

For a css only solution, you can wrap the img in a container where the padding-bottom percentage reserves space on the page until the image loads, preventing reflow.

Unfortunately, this approach does require you to include the image aspect ratio in your css (but no need for inline styles) by calculating (or letting css calculate for you) the padding-bottom percentage based on the image height and width.

If many of your images can be grouped into a few standard aspect ratios, then you could create a class for each aspect ratio to apply the appropriate padding-bottom percentage to all images with that aspect ratio. This may save you a little time and effort if you are not dealing with a wide variety of image aspect ratios.

Following is some example html and css for an image with a 2:1 aspect ratio:

HTML

<div class="container">
  <img id="image" src="https://via.placeholder.com/300x150" />
</div>

CSS

.container {
  display: block;
  position: relative;
  padding-bottom: 50%; /* calc(100%/(300/150)); */
  height: 0;
}

.container img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

The snippet below adds some extra html, css and javascript to create some visual top and bottom reference points and mimic a very slow loading image so you can visually see how the reflow is prevented with this approach.

const image = document.getElementById('image');
const source = 'https://via.placeholder.com/300x150';
const changeSource = () => image.src = source;

setTimeout(changeSource, 3000);
.container {
  display: block;
  position: relative;
  padding-bottom: 50%; /* calc(100%/(300/150)); */
  height: 0;
}

.container img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.top, .bottom {
  background-color: green;
  width: 100%;
  height: 20px;
}
<div class="top"></div>
<div class="container">
  <img id="image" src="" />
</div>
<div class="bottom"></div>
benvc
  • 14,448
  • 4
  • 33
  • 54
  • 1
    @Ry- I think you are right that there really is no simple css only approach to prevent reflow from images of varying aspect ratios. Updated this answer for my own reference and so that I can come back to this for testing purposes when and if more preprocessor features are added to native css down the road. If you stumble on something great (unfortunately, none of the answers to date - mine included - match that description), please post it. – benvc Jul 27 '18 at 00:39
2

At first I would like to write about the answer from october 2013. This was incomplete copied and because of them it is not correct. Do not use it. Why? We can see it in this snippet (scroll the executed snippet to the bottom):

$('img').each(function() { 
    var aspect_ratio = $(this).attr('height') / $(this).attr('width') * 100;
    $(this).wrap('<div style="padding-bottom: ' + aspect_ratio + '%">');
});
img { max-width: 100%; height: auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="width:300px;border:1px solid red">
<img width="400" height="300" src="data:image/png;base64,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"/>
Some text
</div>

And we can see the text is afar from bottom. What is in this example incomplete/ incorrect? I will show it with correct example with pure JavaScript (we do not need to download jQuery for that).

Correct example with pure JavaScript

Please scroll the executed snippet to the bottom.

var imgs = document.querySelectorAll('img');
for(var i = 0; i < imgs.length; i++)
{
    var aspectRatio = imgs[i].getAttribute('height') /
                      imgs[i].getAttribute('width') * 100;

    var div = document.createElement('div');
    div.style.paddingBottom = aspectRatio + '%';
    imgs[i].parentNode.insertBefore(div, imgs[i]);
    div.appendChild(imgs[i]);
}
.restrict-container div{position:relative}
img
{
    position:absolute;
    max-width:100%;
    top:0; left:0;
    height:auto
}
<div class="restrict-container" style="width:300px;border:1px solid red">
    <img width="400" height="300" src="data:image/png;base64,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"/>
    Some text<br>
    <img width="400" height="300" src="data:image/png;base64,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"/>
    Some text
</div>

The mistake from answer from october 2013: the image should be placed absolute (position:absolute) to the wrapped container but it is not so placed.

This is the end of my answer to this question.


For further information read more about:

Bharata
  • 13,509
  • 6
  • 36
  • 50
  • Yes, `position: absolute` was missing from that answer. The rest of this is not relevant to the question, sorry. – Ry- Jul 25 '18 at 09:10
  • 1
    @Ry-, why is that irrelevant? And why I got "-1" for my answer? And at the very least I wrote the correct version with pure JS. It is not fair! – Bharata Jul 25 '18 at 09:17
  • You don't understand why everything after "With HTML5 we could do it differently" doesn't help with the problem? – Ry- Jul 25 '18 at 16:10
  • 1
    @Ry-, you are right, this sentence is wrong, but I have meant it differently. I am not native english speaker too. I changed my answer acordingly to this sentence. I hope now is my answer fully OK? – Bharata Jul 25 '18 at 18:13
  • Adding side information to an answer is fine, but I don’t think 75% side information makes for a good answer. It could be cut down to the correct JavaScript example. – Ry- Jul 26 '18 at 07:45
  • 1
    @Ry-, do you mean that I should delete all text after the sentence "This is the end of my answer to this question."? – Bharata Jul 26 '18 at 07:49
  • Yes, I think it makes the answer less confusing while being equally helpful. – Ry- Jul 26 '18 at 07:53
2

If I understand the requirements ok, you want to be able to set an image size, where this size is known only on content (HTML) generation, so it can be set as inline styles.

But this has to be independent of the CSS, and also prior to image loading, so also independent from this image sizes.

I have come to a solution tha involves wrapping the image in a div, and including in this div an svg that can be set to have proportions directly as an inline style.

Obviously this is not much semantic, but at least it works

The containing div has a class named img to show that it , well, should be an img

To try to reproduce the loading stage, the images have a broken src

.container {
  margin: 10px;
  border: solid 1px black;
  width: 200px;
  height: 400px;
  position: relative;
}

.img {
  border: solid 1px red;
  width: fit-content;
  max-width: 100%;
  position: relative;
}

svg {
  max-width: 100%;
  background-color: lightgreen;
  opacity: 0.1;
}

#ct2 {
  width: 500px;
}

.img img {
  position: absolute;
  width: 100%;
  height: 100%;
  max-height: 100%;
  max-width: 100%;
  top: 0px;
  left: 0px;
  box-shadow: inset 0px 0px 10px blue;
}
<div class="container" id="ct1">
    <div class="img">
        <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 400 300" width="400">
        </svg>
      <img width="400" height="300" src="missing.jpg">
    </div>
</div>
<div class="container" id="ct2">
    <div class="img">
        <svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 40 30" width="400">
        </svg>
      <img width="400" height="300" src="missing.jpg">
    </div>
</div>
vals
  • 61,425
  • 11
  • 89
  • 138
  • Yep, the general idea of an inline SVG with the same dimensions is probably the most reliable and flexible way. Thanks. – Ry- Oct 23 '18 at 22:33
1

I find the best solution is to create a transparent base64 gif with corresponding dimensions as a placeholder for img tags where loading is triggered via js after page is loaded.

<img data-src="/image.png" src="data:image/gif;base64,R0lGODlhyAAsAYABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">

For blog posts and such I use this PHP function to create them automatically

function CreatePreloadPlaceholderGif($width, $height) {
    $wHex = str_split(str_pad(dechex($width), 4, "0", STR_PAD_LEFT), 2);
    $hHex = str_split(str_pad(dechex($height), 4, "0", STR_PAD_LEFT), 2);
    $hex = "474946383961".$wHex[1].$wHex[0].$hHex[1].$hHex[0]."800100ffffff00000021f904010a0001002c00000000010001000002024c01003b";
    $base64= '';
    foreach(str_split($hex, 2) as $pair){
       $base64.= chr(hexdec($pair));
    }
    return base64_encode($base64);
}
echo CreatePreloadPlaceholderGif(300, 500);
//  R0lGODlhLAH0AYABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==

On the frontend the result is something like this

function loadimage() {
      elements = document.querySelectorAll('img[data-src]');
    elements.forEach( el => {
        el.setAttribute('src', el.getAttribute('data-src'))
    });
}
img {
  background-color:#696969;
}
<div>300x500 image placeholder</div>
<img data-src="https://ibec.or.id/wp-content/uploads/2018/10/300x500.png" src="data:image/gif;base64,R0lGODlhLAH0AYABAP///wAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==">
<div>After page load, run js command to replace src attribute with data-src</div>
<button onclick="loadimage()">Load image</button>
40oz
  • 45
  • 8