<img> sizes attribute doesn't work while loading

Viewed 400

I am using HTMLImageElement.sizes to define the width of my image.

https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/sizes

img {
  background: orange;
  border: 2px solid black;
  aspect-ratio: 1/1;
  object-fit: contain;
}
<img srcset="https://www.dofactory.com/img/html/vangogh-sm.jpg 120w,
            https://www.dofactory.com/img/html/vangogh.jpg 193w,
             https://www.dofactory.com/img/html/vangogh-lg.jpg 278w"
     sizes="(max-width: 710px) 120px,
            (max-width: 991px) 193px,
            278px">

Although the size is set correctly depending on the viewport width the image size works only once the image stars to is fully loaded.

You can see that the image is only 0x0px during load in this video:

I am looking for a dry way which doesn’t require to define and maintain the size once in html and once in css as this is harder to keep in sync. Also the sizes attribute is required for performance reasons: Why can’t we just do this using css or javascript

Is there any trick to use the sizes already before the image has been downloaded?

5 Answers

To solve this problem, you need:

  1. Add css breakpoints with @media.
  2. Set the initial height.
  3. Set aspect-ratio as the original image.

The aspect-ratio only works (in this case) if it has width or height, without both of those properties aspect-ratio doesn't work.

img {
  --initial-height: 360px;
  height: var(--initial-height);
  background: orange;
  border: 2px solid black;
  aspect-ratio: 278/360; /* original image size - width / height */
  object-fit: contain;
}

@media screen and (max-width: 991px) {
  img {
    --initial-height: 193px;
  }
}

@media screen and (max-width: 710px) {
  img {
    --initial-height: 120px;
  }
}
<img srcset="
        https://www.dofactory.com/img/html/vangogh-sm.jpg 120w,
        https://www.dofactory.com/img/html/vangogh.jpg    193w,
        https://www.dofactory.com/img/html/vangogh-lg.jpg 278w
      " sizes="(max-width: 710px) 120px,
            (max-width: 991px) 193px,
            278px" />

You can do a trick to render a block (a div) of a size that fits the aspect ratio of your image and then the image can be placed with a position set to absolute with respect to the block, the block will be relative. This way your image will always be sized with respect to the div and only thing that you're doing by keeping the sizes attribute with the img tag itself that you can do but for the holding block, I mean the div. You can set width for different view-ports for that div and then the image will automatically be scaled with the div.

The only condition for this to work is that the aspect ratio of your images, that you're using in the srcset of img, should be equal. Because right now what I can see is that all the 3 images have different aspect ratio, this you'll need to make equal because then only the images will have width and height scaling proportionately on different view-ports.

For eg: lets say your image is of size 120px X 155px (I'm using your smallest image for explaining the thing, aspect-ratio for this is 24:31), then the holding block (the div) will have width set for different view-ports, the same way you're setting it for your img using the sizes attribute. And then the height can be calculated by giving a bottom-padding of 129.2%.

You can say, css will be as below:

div.holder-element {
  position: relative;
  display: block;
  height: 0;
  padding-bottom: 129.2%;
  overflow: hidden;
  background-color: orange;
}
.top-holder-to-keep-the-layout-okay, .holder-element {
  width: 280px;
}

@media (max-width: 740px) {
  .top-holder-to-keep-the-layout-okay, .holder-element {
    width: 120px;
  }
}

@media (min-width: 992px) and (max-width: 1192px) {
  .top-holder-to-keep-the-layout-okay, .holder-element {
    width: 193px;
  }
}

div.holder-element img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}
<div class="top-holder-to-keep-the-layout-okay">
  <div class="holder-element">
    <img srcset="
          https://www.dofactory.com/img/html/vangogh-sm.jpg 120w,
          https://www.dofactory.com/img/html/vangogh.jpg 193w,
          https://www.dofactory.com/img/html/vangogh-lg.jpg 278w
        " />
  </div>
</div>

Note: Please remember to keep aspect ratio same for all of your images on different view-ports.

With this solution your images will scale properly with the holding div, and div with orange background will anyways be shown even if the image inside it is not loaded.

I hope this will give you some idea to proceed further.

You can keep the sizes of the images according to the div container of your image. And in addition to the comments above you can use picture element rather than the img. You can use multiple source elements.

https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images

<picture>
  <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg">
  <source media="(min-width: 800px)" srcset="elva-800w.jpg">
  <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva">
</picture>

@jantimon If you'll keep your img tag with all the attributes that you have, in a holder div as shown below, then your problem with keeping the size while image is loading will be resolved:

.holder-element {
  display: inline-block;
  min-width: 120px;
  min-height: 155px;
  border: 2px solid black;
  background: orange;
}
.holder-element img {
  aspect-ratio: 1/1;
  object-fit: contain;
}
<div class="holder-element">
    <img srcset="https://www.dofactory.com/img/html/vangogh-sm.jpg 120w,
            https://www.dofactory.com/img/html/vangogh.jpg 193w,
             https://www.dofactory.com/img/html/vangogh-lg.jpg 278w"
          sizes="(max-width: 710px) 120px,
            (max-width: 991px) 193px,
            278px">
</div>

Note: give this solution a try, I hope this will work for you

HTML

<div class="holder-element">
   <img srcset="https://www.dofactory.com/img/html/vangogh-sm.jpg 120w,
        https://www.dofactory.com/img/html/vangogh.jpg 193w,
         https://www.dofactory.com/img/html/vangogh-lg.jpg 278w"

     sizes="(max-width: 740px) 120px,
        (max-width: 991px) 193px,
        278px">
</div>

Style

.holder-element {
  width: 120px;
  height: 360px;
}

.holder-element img {
  width: 120px;
  height: 360px;
  aspect-ratio: 3/4;
  object-fit: contain;
}

@media (min-width: 740px) {
  .holder-element {
     width: 193px;
  }
}

@media (min-width: 992px) and (max-width: 1192px) {
   .holder-element {
      width: 278px;
   }
 }
Related