69

How can I resize and reposition the image inside a box, in such way that it covers the entire box, similar to how background-size: cover works.

<div class="box" style="width: 100px; height: 100px;">
  <img src="pic.jpg" width="413" height="325">
</div>

I know I have to add overflow:hidden to the box and the image needs position: absolute. But what's the formula that gets me the right new size for the image, and left + top positions?

Danield
  • 121,619
  • 37
  • 226
  • 255
Alex
  • 66,732
  • 177
  • 439
  • 641
  • `$('.box').each(function(i) { $(this).children('img:first').height($(this).height()).width($(this).width()).css({ position: 'absolute', top: 0, left: 0 }); })` – SpYk3HH Nov 04 '13 at 20:43
  • @SpYk3HH that's the same as `background: 100% 100%`, not `background-size: cover` – MattDiamant Nov 04 '13 at 20:44
  • Do you want the image to be centered as well or just large enough? I'm looking at this example which doesn't center the image. http://www.w3schools.com/cssref/playit.asp?filename=playcss_background-size&preval=cover – TreeTree Nov 04 '13 at 20:49
  • not really sure what you want as cover is intrinsic to is background positioning. this seems kind of bad posturing for an inner img element, but i suppose you could try an exact formula, though I'm not sure that formula myself – SpYk3HH Nov 04 '13 at 20:50
  • @TreeTree: yes. my mistake then. I thought that background-size cover will center the image as well :P (maybe background-position: center is required too?) – Alex Nov 04 '13 at 21:19
  • I believe setting it to center will only work if the image is smaller than the container. If your image is twice the size of the container, it'll still be positioned to the top left corner. – TreeTree Nov 04 '13 at 21:29

15 Answers15

175

For what it's worth: this can now be done with CSS alone with...

The new CSS property object-fit (Current browser support)

Just set object-fit: cover; on the img

You don't even need to wrap the img in a div!

FIDDLE

img {
  width: 100px;
  height: 100px;
}
.object-fit {
  display: block;
  object-fit: cover;
}
.original {
  width: auto;
  height: auto;
  display: block;
}
<img src="http://lorempixel.com/413/325/food" width="413" height="325">
<p>Img 'squashed' - not good</p>
<img class="object-fit" src="http://lorempixel.com/413/325/food" width="413" height="325">
<p>object-fit: cover -
   The whole image is scaled down or expanded till it fills the box completely, the aspect ratio is maintained. This normally results in only part of the image being visible. </p>
<img class="original" src="http://lorempixel.com/413/325/food" width="413" height="325">
<p>Original ing</p>

You can read more about this new property in this webplatform article.

Also, here is a fiddle from the above article which demonstrates all the values of the object-fit property.

dǝɥɔS ʇoıןןƎ
  • 1,674
  • 5
  • 19
  • 42
Danield
  • 121,619
  • 37
  • 226
  • 255
  • 25
    I don't know how this is getting so many votes with no support for IE11 – Ricky Boyce Aug 13 '15 at 01:21
  • 8
    Ofcourse i do, but am forced to treat IE like a loving father. – Ricky Boyce Oct 17 '15 at 19:07
  • @Toskan This isn't the accepted answer because of the lack of IE support. IE sucks and all, but if it's completely unsupported in it; then it's not a fully compatible solution. There are still plenty of organizations still stuck on IE, which is unfortunate, but still makes it a requirement for basic compatibility standards. The use is trailing off though, and I hope IE isn't a factor in the future, because of situations just like this one. – FiLeVeR10 Dec 22 '17 at 16:01
  • 5
    You can always use a polyfill for those obsolete browsers https://github.com/bfred-it/object-fit-images – Jair Reina Sep 11 '18 at 18:38
  • 1
    IE11 usage stats are declining each month... this answer shall become the accepted one. – Markus Siebeneicher Apr 01 '20 at 17:26
  • @RickyBoyce many people just neglect IE since it is famous for its anomaly – Mohammed Shareef C Nov 10 '20 at 13:35
57

Close enough, pure CSS solution for background size cover simulation using img tag with very good browser support (IE8+):

.container {

  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;

  overflow: hidden;

}

.container img {

  position: absolute;
  top: 50%;
  left: 50%;

  width: auto;
  height: auto;

  max-height: none;
  max-width: none;

  min-height: 100%;
  min-width: 100%;

  transform: translate(-50%, -50%);
  -ms-transform: translate(-50%, -50%);
  -webkit-transform: translate(-50%, -50%);

}
<div class="container">
  <img src="//lorempixel.com/400/200/sports/1/" />
</div>
Jorjon
  • 5,316
  • 1
  • 41
  • 58
Vlatko
  • 1,385
  • 1
  • 18
  • 35
  • 1
    Great solution! Except it does not work on IE8. This works on IE9+. More info here: http://caniuse.com/#feat=transforms2d – Junaid Bhura Jul 25 '16 at 04:45
  • When I use your solution, the image simply stretches to fit the container. Am I doing something wrong? – bumbleshoot Aug 26 '17 at 02:44
  • 1
    @torjinx It seems this solution only expands smaller images, it doesn't contract images that are much larger than the container. – bendman Jul 16 '18 at 16:06
  • This should be the accepted answer! Easy, supported everywhere and does exactly what it's supposed to. Thank you! – vedsil Dec 14 '20 at 15:10
10

this may be easier

jQuery

$('.box').each(function() {
    //set size
    var th = $(this).height(),//box height
        tw = $(this).width(),//box width
        im = $(this).children('img'),//image
        ih = im.height(),//inital image height
        iw = im.width();//initial image width
    if (ih>iw) {//if portrait
        im.addClass('ww').removeClass('wh');//set width 100%
    } else {//if landscape
        im.addClass('wh').removeClass('ww');//set height 100%
    }
    //set offset
    var nh = im.height(),//new image height
        nw = im.width(),//new image width
        hd = (nh-th)/2,//half dif img/box height
        wd = (nw-tw)/2;//half dif img/box width
    if (nh<nw) {//if portrait
        im.css({marginLeft: '-'+wd+'px', marginTop: 0});//offset left
    } else {//if landscape
        im.css({marginTop: '-'+hd+'px', marginLeft: 0});//offset top
    }
});

css

.box{height:100px;width:100px;overflow:hidden}
.wh{height:100%!important}
.ww{width:100%!important}

This should handle any size/orientation, and will not only resize, but offset the images. All without relative or absolute positioning.

made a fiddle: http://jsfiddle.net/filever10/W8aLN/

FiLeVeR10
  • 2,155
  • 1
  • 12
  • 13
  • Hi. I would like to use this good example. But it does not work when the parent div is hidden with css visibility. Anyone got a solution for that? – Mr Rebel Sep 02 '14 at 11:37
  • @MrRebel You mean like this? jsfiddle.net/filever10/by91rqr5 shown working on one element but redefine `el` and you can affect multiple, ie. `var el = $(this).children('.box');` – FiLeVeR10 Sep 04 '14 at 02:05
  • The html structure are like this http://jsfiddle.net/mpkLtn4q/ (The button don´t work on this fiddle cause of the new html structure) – Mr Rebel Sep 04 '14 at 11:13
  • 1
    @MrRebel Yeah, the button *don't work* because `line 28` needs to be updated as well from `$(this).siblings('.box');` to `$(this).siblings('ul').children('.box');` it does mention that in the comments on that line... **"//define the element in relation to the action"**. The button is just a general representation to perform some action that triggered the change, you can make it `$('.nedmnthnguwnt')` for the event to occur. http://jsfiddle.net/filever10/jnrsrcr3/ – FiLeVeR10 Sep 04 '14 at 21:14
  • 2
    I like this method, however I had to swap the classes that were added and removed, as well as put in another condition into the if statements, if (ih>iw || th>tw), and if (nhtw). – Mike Kormendy Jan 28 '16 at 05:35
  • 2
    Liked it :) Updated for any size container (not only squares): http://jsfiddle.net/W8aLN/265/ – Julia Mar 02 '16 at 19:33
  • @Julia Awesome! This was actually code from a site where I had to do fullscreen backgrounds and sniff the size ratio, I dumbed it down to just handle squares for this answer. Just remember, this solution depends on image sizes, so if you run a bunch of images, or really large images, it's likely to execute before the images have rendered. Because of that, it's best to actually have height and width attributes on the images and use those instead of sniffing the image size, that way you don't have to wait for them to load to get the size and execute. – FiLeVeR10 Mar 11 '16 at 12:54
  • 1
    Wow :) Well, I had to re-invent this for fullscreen fixed background images, because IE behavior when use background-size:cover is buggy. And yes, I forgot to mention that images should be loaded first, I used a small plugin for that, imagesloaded.js – Julia Mar 11 '16 at 15:38
  • You can do it with CSS only, see answers below. – Sandwell Mar 05 '18 at 16:18
  • @Sandwell The css only solution is still not a universal solution as it has no support in IE 11. – FiLeVeR10 Jul 17 '18 at 13:38
  • @FiLeVeR10 it's just min-width and min-height: 100%, IE supports this. – Sandwell Jul 18 '18 at 14:06
  • @Sandwell the css only solution doesn't use min-width/min-height. It uses `object-fit: cover` which is not supported in most versions of IE. Show an example of what you mean. – FiLeVeR10 Oct 02 '18 at 13:17
6

Also for what it's worth, the same effect can be produced by instead of setting "width" and "height" (setting them could break this approach btw):

min-width: 100%; min-height: 100%;

or

min-width: (your desired percent of viewport width)vw; min-height: (your desired percent of viewport height)vh;

with

overflow: hidden;

on the parent

:)

SirRodge
  • 594
  • 5
  • 8
3

The idea is to make additional wrapper for image:

<div class="wrap">
  <div class="inner">
    <img src="http://placehold.it/350x150">
  </div>
</div>

And use such CSS:

.wrap {
  position: relative;
  width: 100%;
  height: 200px;
  background: rgba(255, 0, 0, 0.3);
  overflow: hidden;
}

.inner {
  position: absolute;
  min-width: 100%;
  height: 100%;
  left: 50%;
  -moz-transform: translateX(-50%);
  -o-transform: translateX(-50%);
  -ms-transform: translateX(-50%);
  -webkit-transform: translateX(-50%);
  transform: translateX(-50%);
}

.inner img {
  position: absolute;
  min-height: 100%;
  min-width: 100%;
  top: 50%;
  left: 50%;
  -moz-transform: translate(-50%, -50%);
  -o-transform: translate(-50%, -50%);
  -ms-transform: translate(-50%, -50%);
  -webkit-transform: translate(-50%, -50%);
  transform: translate(-50%, -50%);
}

This is working example: https://jsfiddle.net/kr60jroe/

  • 2
    Doesn't seem to work for all cases in your example. For example the 3000x2010 image goes way beyond the bounds of it's container. – Jake Wilson Mar 01 '17 at 18:50
2

From https://developer.mozilla.org/en-US/docs/Web/CSS/background-size:

cover
    This keyword specifies that the background image should be scaled to be as small as possible while ensuring both its dimensions are greater than or equal to the corresponding dimensions of the background positioning area.

So, you're either looking at making the width: 100% or the height: 100%, whichever will create an overlap within the parent div. So we can use the following logic:

var makeBackgroundCover = function (div) {
    $(div + " img").css("height", "100%");
    if ($(div + " img").width() < $(div).width()) {
        $(div + " img").css({
            "height": "auto",
            "width": "100%"
        });
    }
}

The following fiddle shows this function working on both a horizontal and vertical image.

http://jsfiddle.net/2r5Cb/

MattDiamant
  • 8,561
  • 4
  • 37
  • 46
  • 1
    does this center the image as well, like `background-size: cover` would? – FiLeVeR10 Nov 04 '13 at 21:11
  • `background-size: cover` does not center the image. If you look at my jsfiddle, you'll see that I've included `background-size: cover` comparison images to show that this function creates an identical reproduction. – MattDiamant Nov 04 '13 at 21:13
  • 1
    you're totally right, but it looks like the original question wanted that that happen, or it wouldn't ask for size *and* positioning. – FiLeVeR10 Nov 04 '13 at 21:21
  • Great answer, I like that you followed the actual spec. – Daniel Dewhurst Apr 13 '17 at 10:25
2

Here is my approach:

//collect the nodes
var parent = $('.box');
var img = $('image', box);

//remove width and height attributes
img.removeAttr('width');
img.removeAttr('height');

//set initial width
img.attr('width', parent.width());

//if it's not enough, increase the width according to the height difference
if (img.height() < parent.height()) {
    img.css('width', img.width() * parent.height() / img.height());
}

//position the image in the center
img.css({
    left: parseInt((img.width() - parent.width())/-2) + 'px',
    top: parseInt((img.height() - parent.height())/-2) + 'px'
});

FIDDLE

matewka
  • 9,912
  • 2
  • 32
  • 43
  • I like this solution, because it's less typing. Though it will calculate and set the dimensions *twice* on an image if it's not correct on the first sizing. – FiLeVeR10 Sep 04 '14 at 02:03
2

Here's a clean JavaScript function to do this and an example of implementation:

function backgroundCover(elementSizes, containerSizes) {
    var elementRatio = elementSizes.width / elementSizes.height,
        containerRatio = containerSizes.width / containerSizes.height;
        width = null,
        height = null;
    if (containerRatio > elementRatio) {
        width = Math.ceil( containerSizes.width );
        height = Math.ceil( containerSizes.width / elementRatio );
    } else {
        width = Math.ceil( containerSizes.height * elementRatio );
        height = Math.ceil( containerSizes.height );
    }
    return { width, height };
}

Here's an example of implementation:

HTML

<!-- Make sure the img has width and height attributes. The original image's width and height need to be set in order to calculate the scale ratio. -->
<div class="photo"><img src="photo.jpg" width="400" height="300"></div>

CSS

.photo {
    position: relative;
    overflow: hidden;
    width: 200px;
    padding-bottom: 75%; /* CSS technique to give this element a 4:3 ratio. */
}
.photo img {
    position: absolute;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-50%, -50%);
    -moz-transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
}

JavaScript

$( window ).on( 'resize', function() {
    $( '.cover-photo' ).each( function() {
        var img = $( 'img', this ),
            imgWidth = img.attr( 'width' ),
            imgHeight = img.attr( 'height' ),
            containerWidth = $( this ).width(),
            containerHeight = $( this ).height(),
            newSizes = backgroundCover( { width: imgWidth, height: imgHeight }, { width: containerWidth, height: containerHeight } );
        img.css( {
            width: newSizes.width,
            height: newSizes.height
        } );
    } );
} );
Gavin
  • 7,544
  • 4
  • 52
  • 72
1

While reading the accepted answer, it strikes me that we simply test on whether the image is 'portrait' or 'landscape':

   if (ih>iw) {//if portrait

In the case of the OP, that's right. But others might be dealing with rectangles and should take the aspect ratio of the container and the 'child'-image into consideration:

    var int_container_width  = parseInt( $_container.width()  );
    var int_container_height = parseInt( $_container.height() );
    var num_container_aspect = int_container_width/int_container_height;

    var int_image_width      = parseInt( $_image.width() );
    var int_image_height     = parseInt( $_image.height());
    var num_image_aspect     = int_image_width/int_image_height;

    if ( num_image_aspect > num_container_aspect){
      num_scale = int_container_width/int_image_width * 100;
    } else {
      num_scale = int_container_height/int_image_height * 100;
    }
Ideogram
  • 1,265
  • 12
  • 21
1

This is a pure css solution. You can define a wrapper with:

div.cover {
  position: fixed; 
  top: -50%; 
  left: -50%; 
  width: 200%; 
  height: 200%;
}

and the img:

img.cover {
  position: absolute; 
  top: 0; 
  left: 0; 
  right: 0; 
  bottom: 0; 
  margin: auto; 
  min-width: 50%;
  min-height: 50%;
  overflow-x: hidden;
}

Here the live example:

http://codepen.io/ErwanHesry/pen/JcvCw

Ruben Rizzi
  • 342
  • 1
  • 3
  • 20
  • This only works if the image is smaller than it's container(in this case `.box`). This will make an image grow to fit it's container, but won't make it shrink to fit it's container. – hal Jun 30 '15 at 17:46
  • 1
    Of course it is not a perfect solution, but it is probably the only that uses no javascript. Maybe you can overcome the problem using a very small svg image. – Ruben Rizzi Jul 01 '15 at 11:01
1

You can use this style to the image tag :"object-fit:cover;" This link will support you also https://css-tricks.com/almanac/properties/o/object-fit/

0

If you want the image centered in the box without resizing the image, just use this code:

.box {
    width: 100px;
    height: 100px;
    overflow: hidden;
    position: relative;
}
.box img {
    width: 413px;
    height: 325px;
    position: absolute;
    left: 50%;
    top: 50%;
}

If you are looking to resize the image to fit, use the following code:

.box {
    width: 100px;
    height: 100px;
}
.box img {
    width: 100%;
    height: auto;
}

This code will leave some white space if the image is wider than it is tall. If neither of these work, you could just set the image as a background and use background-size: cover;.

zsaat14
  • 1,110
  • 2
  • 10
  • 20
  • 1
    what happens if the image is landscape? – FiLeVeR10 Nov 04 '13 at 21:09
  • The first bit of code will be like looking at the picture through a small hole, so as long as the picture is bigger than the box, the box will be filled. For the second bit of code, a landscape image will leave space below it (and above depending on your padding). To avoid this, you could add a second image and remove all padding and margins. – zsaat14 Nov 05 '13 at 03:39
0

For anyone who happens across this answer as I did today looking for a solution that will work with landscape, portrait, rectangle, square, etc images and arbitrary container sizes, I have included my own code below.

This will also work responsively, you'll just need to run it again whenever the window resizes.

JSFiddle:

http://jsfiddle.net/66c43ao1/

HTML

<div class="test">
    <div class="cover">
        <img src="http://d2ws0xxnnorfdo.cloudfront.net/character/meme/cool-dog.jpg" width="590" height="590"/>
    </div>
</div>

CSS

/* modify the width and height below to demonstrate coverage */
.test {
    height: 300px;
    position: relative;
    width: 500px;
}
/* you will need the below styles */
.cover {
    height: 100%;
    left: 0;
    overflow: hidden;
    position: absolute;
    top: 0;
    width: 100%;
    z-index: 1;
}

JS

$('.cover').each(function() {
    var containerHeight = $(this).height(),
        containerWidth  = $(this).width(),
        image           = $(this).children('img'),
        imageHeight     = image.attr('height'),
        imageWidth      = image.attr('width'),
        newHeight       = imageHeight,
        newWidth        = imageWidth;

    if (imageWidth < containerWidth) {
        // if the image isn't wide enough to cover the space, scale the width
        newWidth        = containerWidth;
        newHeight       = imageHeight * newWidth/imageWidth;
    }
    if (imageHeight < containerHeight) {
        // if the image isn't tall enough to cover the space, scale the height
        newHeight       = containerHeight;
        newWidth        = imageWidth * newHeight/imageHeight;
    }

    var marginLeft      = (newWidth - containerWidth)/2;
    var marginTop       = (newHeight - containerHeight)/2;

    image.css({
        marginLeft  : '-' + marginLeft + 'px',
        marginTop   : '-' + marginTop + 'px',
        height      : newHeight,
        width       : newWidth
    });
});

You can of course use libraries such as Backstretch which do this same thing, but I found this solution to be better for my purposes (no increase in dependencies, lighter weight, etc).

Heath
  • 408
  • 3
  • 4
0

I created a function below that should do it. I borrowed some of the logic from the accepted answer and adjusted it to work with any container by creating a ratio for image dimension : container dimension and then compared which is greater to figure which dimension to adjust. Also added a 'center' argument ('true' centers, false sets it to top/left).

I'm using CSS3 with the translateX/Y, but could get it working without it easily enough.

Here's the code:

var coverImage = function(wrap, center) {

  if (typeof center === 'undefined') {
    center = true;
  }

    var wr = $(wrap),
        wrw = wr.width(),
        wrh = wr.height();

  var im = wr.children('img'),
        imw = im.width(),
        imh = im.height();

  var wratio = wrw / imw;
    var hratio = wrh / imh;

  //Set required CSS
  wr.css({'overflow' : 'hidden'});
  im.css({'position' : 'relative'});


  if (wratio > hratio) {
    im.width(wrw);
    im.css({'height' : 'auto'});

    if (center) {
      im.css({
        'top' : '50%',
        'transform' : 'translateY(-50%)'
      });
    }
  } else {
    im.height(wrh);
    im.css({'width' : 'auto'});

    if (center) {
      im.css({
        'left' : '50%',
        'transform' : 'translateX(-50%)'
      });
    }
  }
}

and checkout the jsfiddle to see it in action: https://jsfiddle.net/cameronolivier/57nLjoyq/2/

Community
  • 1
  • 1
0

I made something could work to emulate a background-size:cover and background-position:center.

If you want to change the position just change the styles "top" an "left" of the img

CSS

.box{
    overflow:hidden;
    position:relative;
}

.box img{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    -ms-transform: translate(-50%, -50%);
    -webkit-transform: translate(-50%, -50%);
}

JS

$('.box').each(function() {
     //aspect ratio of container
     var boxRatio = $(this).height() / $(this).width(); 
     //aspect ration of image
     var imageRatio = $(this).children('img').height() / $(this).children('img').width();
     //set width or height 100% depend of difference
     if (imageRatio > boxRatio) {
          $(this).children('img').css({"width":"100%","height":"auto"});                
     } else {
          $(this).children('img').css({"height":"100%","width":"auto" });
     }
});

This function should be activated on "load" and "resize" event.

santillanix
  • 1,947
  • 18
  • 17