0

I realise this has been asked many times under many guises, but most solutions seem to not care about the resulting image height, or only scale in one direction. This:

min-width: 100%;
height: auto;

isn't a solution.

I'm trying to fit an image to the height and width so that it can be dynamically injected into a number of places in different systems and sites, so I don't necessarily know what the parent object is going to be - might be a div with or without overflow set, or a p, or the body - or its dimensions; The parent container mustn't change size just by injecting my image/script .. jQuery is NOT available.

I can't see how the css3 background technique would work.

Proportional scaling is as easy as using a ratio:

<img onload="scale(this)" src="screenshot.png">
<script type="text/javascript">
function scale(img){
var p = img.parentNode,
    x = p.offsetWidth,
    y = p.offsetHeight,
    w = img.naturalWidth,
    h = img.naturalHeight;
    ratio = h / w;
    if (h >= y) {
        h = y;
        w = h / ratio;
        img.style.width = w + "px";
        img.style.height = h + "px";
    } else if(w >= x) {  //} && ratio <= 1){
        w = x;
        h = w * ratio;
        img.style.width = w + "px";
        img.style.height = h + "px";
    }
}
</script>

but this only scales in one plane - I need it to continue to scale both width and height until it fits into the parent container space neatly in both dimensions. If the image is smaller than the parent container, it should scale UP to fill proportionally.

Community
  • 1
  • 1
frumbert
  • 2,323
  • 5
  • 30
  • 61
  • 1
    What if the image is 800x600 (horiz) and the parent is i.e: 200x800 (vert) ? – Roko C. Buljan Nov 16 '15 at 01:06
  • 1
    Are you saying `background-size:contain;` is not what you are looking for? – Donnie D'Amato Nov 16 '15 at 01:07
  • unless the aspect ratios of the containers to the pictures are EXACTLY the same you are going to either get terrible artifacting and stretching/compression along one axis or you are going to have to have letterboxing or pillars. –  Nov 16 '15 at 01:59
  • The reason why they only set one dimension is to make sure it is proportional. – Shaun Nov 16 '15 at 02:05

2 Answers2

3

Adapt image to fit parent container (of unknown size) without stretching image


Instead of messing with JS, let CSS do the job!
  • Set your image to 100%; height and width;
  • Replace your image's src with a transparent pixel and set it's background to the actual src.
  • Use CSS's background-size on your (now) full-sized image:

contain (image will cover the parent completely)
cover (image will respect image proportions instead)

Example using contain

function scale( img ) {
  if(img.isScaled) return;
  img.style.background="no-repeat url("+ img.src +") 50%";
  img.style.backgroundSize="contain";  // Use "contain", "cover" or a % value
  img.style.width="100%";
  img.style.height="100%";
  img.isScaled=true; // Prevent triggering another onload on src change
  img.src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
}
<div style="width:300px; height:300px; background:#000; margin:10px;">
  <img onload="scale(this);" src="//placehold.it/1200x600/f0f">
</div>


<div style="width:300px; height:300px; background:#000; margin:10px;">
  <img onload="scale(this);" src="//placehold.it/400x600/cf5">
</div>

Tested in: FF, CH, E, IE(11), SA


NB: The transparent pixel data is used solely to prevent the browser's Missing Iimage Icon that would be otherwise caused by doing img.src = "";

http://caniuse.com/#search=background-size

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • @RokoC.Bujan Would it matter if the images were `display: inline-block` or `block`? – zer00ne Nov 16 '15 at 02:36
  • @zer00ne Does not matters. If the above works with `inline` (default) images, why would `block` change anything? – Roko C. Buljan Nov 16 '15 at 03:01
  • @RokoC.Bujan Until browsers are unified under stricter standards, I take nothing for granted. I'm not sure whether `block` makes a difference, but I do recall that `inline` and `inline-block` handle height differently than `block` does. Thank you for this light and simple solution. – zer00ne Nov 16 '15 at 07:41
  • @zer00ne You're welcome ;) I've tested the above in Ch, Ff, Sa, E, IE11 (IE8 & IE7 emulated) and it worked. – Roko C. Buljan Nov 16 '15 at 16:48
0

I used this code to fit a video to its parent:

      var parent = vid.parentNode,
      vw = vid.videoWidth,
      vh = vid.videoHeight,
      vr = vh / vw, // video ratio
      pw = parent.offsetWidth,
      ph = parent.offsetHeight,
      pr = ph / pw; // parent ratio
      if(pr > vr) {
        vid.style.width = 'unset';
        vid.style.height = `${ph}px`;
      } else {
        vid.style.height = 'unset';
        vid.style.width = `${pw}px`;
      }
Jacob
  • 527
  • 1
  • 5
  • 14