How to Allocate Space for an Image on a Responsive Webpage

One of my pet peeves is when I'm trying to read a blog post or news article on my phone and scroll partway down the page, an image appears above where I'm looking, pushing the paragraph I'm reading off the screen forcing me to lose my place. It's jarring and annoying so when that happens, I will often try to get the information I'm looking for somewhere else.

Bad example

In the scrollable div below, the browser doesn't know how high the <img> should be when the page first loads, so it goes from very short to very tall once the image is loaded. I added a few seconds of delay to illustrate this phenomenon. Reload the page or click the "reload image" button to see the second paragraph jump around.

Here's some text

Here's some text that will jump around.

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

The HTML looks like this:

<div class="jumpy-image-example">
  <p>some text</p>
  <img class="jumpy-image-example__image" src="my-cool-image.jpg" />
  <p>Lorem ipsum whatever</p>
</div>

And there's a bit of CSS to make the image the width of its parent and have the right height to maintain the original aspect ratio.

.jumpy-image-example__image {
  max-width: 100%;
  height: auto
}

How to get rid of the jumpiness

All we need to know is the dimensions of our image, how to calculate a percentage, and how the padding CSS attribute works.

CSS gives us the ability to set the height and width of any element on the page. However, height and width can't be used to define an aspect ratio. An element's width can be set based on the width of its parent, the viewport dimensions, or a constant, but not based on its own height. However, if we set padding-top to a percentage x, the top padding of the element will be x% of the element's width. If the percentage we use is the aspect ratio of our image, and the height of the element is 0, we can make an element the exact size our image without actually loading the image itself.

Proof

The box below is 100% of the width of its parent, 0px high, and has padding-top: 50%. That means its outer height will always be 50% of its width. If you're on a desktop, try resizing the browser window to see it change height.

Fixing the bad example

Now we know how to make a container for our <img> that is the exact size we want. In this case the image is 1920×1280 pixels. To find the percentage, we just need to calculate height ÷ width × 100% which in this case is 66.6666667%. With that, we can start making changes to our HTML and CSS.

First we need to add our container:

<div class="jumpy-image-example">
  <p>some text</p>
  <div class="jumpy-image-example__image-container" style="padding-top:66.6666667%">
    <img class="jumpy-image-example__image" src="my-cool-image.jpg" />
  </div>
  <p>Lorem ipsum whatever</p>
</div>

and a bit more CSS:

.jumpy-image-example__image-container {
  position: relative;
}

.jumpy-image-example__image-container > .jumpy-image-example__image {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 100%;
  height: 100%;
}

Now we have a container that's the size we need, and an image that's 100% of the height and width of the container.

Good example

Click the "Reload image" button to see what happens before the browser has loaded the image.

Here's some text

This will stay in the same place!

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

That's much better! Now, our <img> will always be the right size even if the image takes a long time to load or fails to load entirely. It also works on any screen width and makes fancier things like lazy loading possible.

Conclusion

I can't really think of a situation where it's a bad idea to do this. Calculating the aspect ratios of individual images can be tedious, but it can be a big UX improvement. Since there are a lot of libraries out there that can get an image's height and width, it's pretty easy to automatically set the container padding in an MVC framework or static site generator. I actually already use img-size in the framework I use for this blog. I may write about that in a future post.

Thanks for reading and as always if you have any feedback find me on Twitter!