204

I have been trying to sort out how to center an oversized image within a div using css only.

We are using a fluid layout, so the width of the image containers varies as the page width does (height of div is fixed). The image sits within a div, withvalue an inset boxshadow so as to appear as if you are looking through the page at the image.

The image itself has been sized to fill the surrounding div at its widest possible value (the design has a max-width value).

It is pretty easy to do if the image is smaller than the surrounding div:

margin-left: auto;
margin-right: auto;
display: block; 

But when the image is larger than the div it simply starts at the left edge and is off center to the right (we are using overflow: hidden).

We could assign a width=100%, but browsers do a lousy job of resizing images and the web design centers around high quality imagery.

Any ideas on centering the image so that overflow:hidden cuts off both edges evenly?

CalvT
  • 3,123
  • 6
  • 37
  • 54
Tom
  • 2,928
  • 4
  • 28
  • 36

12 Answers12

386

Try something like this. This should center any huge element in the middle vertically and horizontally with respect to its parent no matter both of their sizes.

.parent {
    position: relative;
    overflow: hidden;
    //optionally set height and width, it will depend on the rest of the styling used
}

.child {
    position: absolute;
    top: -9999px;
    bottom: -9999px;
    left: -9999px;
    right: -9999px;
    margin: auto;

}
hyounis
  • 4,529
  • 2
  • 18
  • 16
  • 5
    wonderful: this works with any element. even with html 5 video... thanks! – taseenb Nov 20 '13 at 14:37
  • 3
    As I understand this: it works because it creates an element (hopefully) bigger than the image (2*9999px x 2*9999px) and centers the image inside that element (margin: auto). So as long as the image does not exceed 2*9999px it should work. – Michael Krupp Jun 05 '14 at 17:59
  • 19
    Why not use `-100%` for top/right/bottom/left? With that the container could have literally **any** width. – Simon Jun 17 '14 at 10:03
  • update: I was a bit too fast there... relative size does not work. I put up an example here: http://jsfiddle.net/v5ZFM/2/ and here: http://jsfiddle.net/v5ZFM/3/. See how the first example is not centered, while the second one is. – Michael Krupp Jul 03 '14 at 14:56
  • Only if I knew this before.. works like a charm!! It's unable to count the amount of times I needed this before :) – Joshua - Pendo Aug 23 '14 at 18:07
  • @uszywieloryba I thought everyone dropped support for IE7? @Simon relative size wouldn't work, because then it would be relative to `.parent`. So if the parent is 100x100 and the image is 300x300 using -100% wouldn't work, just like @keks said. – hyounis Sep 03 '14 at 22:19
  • 16
    Yeah I've experienced the `-100%` issue as well ^^. Btw: if you add `min-width` and `min-height` of `100%` you basically get a `background-size: cover;` behaviour with image tags -> [jsfiddle](http://jsfiddle.net/zzur2aqc/) – Simon Sep 09 '14 at 07:55
  • 1
    I don't understand what the 40% padding bottom is supposed to do here. – Mike Kormendy Oct 26 '14 at 21:05
  • 3
    @HarrisonPowers Well the parent has to have a height of course, whether it's fixed or dynamic. It will also work if some other content is filling up the div causing it to have a height (like text for example). – hyounis Nov 15 '14 at 10:52
  • @Simon if you add a `min-height` or `min-width` it would break for smaller images though. – hyounis Nov 15 '14 at 10:52
  • @elleestcrimi If parent is the wrapper for the only child, then mostly parent will not own up the child, having height of 0. Any fix for that? – Ethan Feb 17 '15 at 09:22
  • @Ethan Well here I assume that you give the parent the height and width you'd want the image to be. For example if you wanted your image to be 100x200. Then you'd set the parent's dimensions as 100x200. Does this answer your question? – hyounis Feb 17 '15 at 12:12
  • I nested the .parent and .child components inside a flexbox container, the image disappears unless I remove the .child class. I wonder why: here is the forked [codepen](http://codepen.io/zhangtreefish/pen/xRqPYM/?editors=1100#0) (the lawnContainer is the .parent element). – Treefish Zhang Nov 22 '16 at 14:47
  • fixed height for the container is needed – valerio0999 Mar 02 '17 at 14:47
  • 1
    I just spent _hours_ trying to solve a very specific problem (centering oversized image scaled down by `transform:scale()` css in table `td` cell). Until your answer came to help as a solution. Thank you ma'am! – FurloSK Feb 15 '18 at 16:45
  • The top/bottom (left/right) do not have to be -9999 - they only have to be equal. For example: top:0;bottom:0; – KS74 Apr 14 '20 at 12:14
  • @KS74 the reason I put the extreme negatives is for use case of when the item you are trying to center is bigger than the container. – hyounis Apr 15 '20 at 17:31
  • I know, and I wrote just about the case. – KS74 Apr 16 '20 at 18:31
226

This is an old Q, but a modern (as of 2016) solution without flexbox or position absolute works like this.

margin-left: 50%;
transform: translateX(-50%);

.outer {
  border: 1px solid green;
  margin: 20px auto;
  width: 20%;
  padding: 10px 0;
  /*   overflow: hidden; */
}

.inner {
  width: 150%;
  background-color: gold;
  /* Set left edge of inner element to 50% of the parent element */
  margin-left: 50%; 
  /* Move to the left by 50% of own width */
  transform: translateX(-50%); 
}
<div class="outer">
  <div class="inner">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quos exercitationem error nemo amet cum quia eaque alias nihil, similique laboriosam enim expedita fugit neque earum et esse ad, dolores sapiente sit cumque vero odit! Ullam corrupti iure eum similique magnam voluptatum ipsam. Maxime ad cumque ut atque suscipit enim quidem. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Excepturi impedit esse modi, porro quibusdam voluptate dolores molestias, sit dolorum veritatis laudantium rem, labore et nobis ratione. Ipsum, aliquid totam repellendus non fugiat id magni voluptate, doloribus tenetur illo mollitia. Voluptatum.</div>
</div>

So why does it work?
At first glance it seems that we shift 50% to the right and then 50% to the left again. That would result in zero shift, so what?
But the 50% are not the same, because context is important. If you use relative units, a margin will be calculated as percentage of the width of the parent element, while the transform will be 50% relative to the same element.

We have this situation before we add the CSS

       +---------------------------+
       | Parent element P of E     |
       |                           |
       +-----------------------------------------+
       | Element E                               |
       +-----------------------------------------+
       |                           |
       +---------------------------+

With the added style margin-left: 50% we have

       +---------------------------+
       | Parent element P of E     |
       |                           |
       |             +-----------------------------------------+
       |             | Element E                               |
       |             +-----------------------------------------+
       |             |             |
       +-------------|-------------+
       |>>>>> a >>>>>|
           
       a is 50% width of P

And the transform: translateX(-50%) shifts back to the left

       +---------------------------+
       | Parent element P of E     |
       |                           |
+-----------------------------------------+
| Element E         |                     |
+-----------------------------------------+
|<<<<<<<<< b <<<<<<<|              |
       |            |              |
       +------------|--------------+
       |>>>>> a >>>>|
           
       a is 50% width of P
       b is 50% width of E

Unfortunately this does only work for horizontal centering as the margin percentage calculation is always relative to the width. I.e. not only margin-left and margin-right, but also margin-top and margin-bottom are calculated with respect to width.

Browser compatibility should be no problem: https://caniuse.com/#feat=transforms2d

yunzen
  • 32,854
  • 11
  • 73
  • 106
  • 1
    It does not work for vertical alignment (when .inner has larger height that .outer) – cronfy Dec 12 '16 at 06:59
  • 1
    @cronfy No. vertically it will not work, because `margin-top: 50%` will be 50% of the __width__. not the height as desired. – yunzen Feb 09 '17 at 10:37
  • searching for a solution for a while, best, simpliest one; Cheers – lauWM Dec 24 '17 at 07:48
  • There's a webkit for transform `-webkit-transform`, for those interested you can look up: https://www.quackit.com/css/properties/webkit/css_-webkit-transform.cfm – levelone Feb 09 '18 at 06:39
  • 3
    This is pure wizard magic. Thank you! Works perfectly in Safari and Chrome (with an added `-webkit-transform: translateX(-50%);` for good measure) – Nick Schneble Dec 17 '18 at 19:27
  • Used this for a overflow parallax and can confirm it works. Thanks! – Ledii Aug 17 '19 at 12:49
  • Fantastic @yunzen! Combined with `width: 100%; min-width: 700px`, this stretches for large monitors, and then center-crops when less than 700px. – stwr667 Oct 31 '19 at 12:48
  • seems `translateX` not align with pixel, but `margin` dose. //ubuntu20, chrome94 – yurenchen Oct 12 '21 at 15:14
  • @yurenchen If your png is blurred, it might be that it is scaled in the browser? – yunzen Oct 13 '21 at 09:58
  • @yunzen thanks for your reply. make sure img width,height is right and zoom is 1. seems sometimes translateX let img show not align pixel, etc. it place img at 0.5px //just test on ubuntu20, chrome94 – yurenchen Oct 13 '21 at 11:38
22

Put a large div inside the div, center that, and the center the image inside that div.

This centers it horizontally:

HTML:

<div class="imageContainer">
  <div class="imageCenterer">
    <img src="http://placekitten.com/200/200" />
  </div>
</div>

CSS:

.imageContainer {
  width: 100px;
  height: 100px;
  overflow: hidden;
  position: relative;
}
.imageCenterer {
  width: 1000px;
  position: absolute;
  left: 50%;
  top: 0;
  margin-left: -500px;
}
.imageCenterer img {
  display: block;
  margin: 0 auto;
}

Demo: http://jsfiddle.net/Guffa/L9BnL/

To center it vertically also, you can use the same for the inner div, but you would need the height of the image to place it absolutely inside it.

JFK
  • 40,963
  • 31
  • 133
  • 306
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • This ended up with the left edge of the image centered within the "imageContainer". As I commented above, our image container div is fluid width and fixed height. – Tom Jan 28 '13 at 13:28
  • 1
    @Tom: If I remove the `width` setting on the container, it will be the width of the parent, and the image is centered even if the page is narrower than the image, i.e. as much of the left edge as the right edge will be hidden: http://jsfiddle.net/Guffa/L9BnL/2/ – Guffa Jan 28 '13 at 13:37
  • Thanks for the inspiration. I use position: relative for .imageCenter. This way I don't need the position: relative for the parent container. – user3153298 Jun 18 '15 at 15:22
  • There's a lot of clever tricks to this question and they've all got their drawbacks. Some work "better" but are not at all easy to understand. This solution makes perfect sense, the image is being centered normally in a wider div that is being cropped. It feels wrong, but it definitely works without breaking the laws of web-physics. I chose this option over several other very difficult-to-understand tricks, even if it does feel a bit dirty. – Radley Sustaire Jan 17 '18 at 04:30
10

Late to the game, but I found this method is extremely intuitive. https://codepen.io/adamchenwei/pen/BRNxJr

CSS

.imageContainer {
  border: 1px black solid;

  width: 450px;
  height: 200px;
  overflow: hidden;
}
.imageHolder {
  border: 1px red dotted;

  height: 100%;
  display:flex;
  align-items: center;
}
.imageItself {
  height: auto;
  width: 100%;
  align-self: center;

}

HTML

<div class="imageContainer">
  <div class="imageHolder">
    <img class="imageItself" src="http://www.fiorieconfetti.com/sites/default/files/styles/product_thumbnail__300x360_/public/fiore_viola%20-%202.jpg" />
  </div>
</div>
aioobe
  • 413,195
  • 112
  • 811
  • 826
ey dee ey em
  • 7,991
  • 14
  • 65
  • 121
  • 4
    Nice! It can be simplified, too. The trick is done by `display: flex` on the parent container and `align-self: center` on the image. Here's an optimized version: https://codepen.io/anon/pen/dWKyey – caiosm1005 May 14 '17 at 13:51
7

It's simple with some flex and overflow set to hidden.

enter image description here

<!DOCTYPE html>
<html lang="en">
<head>
    <style>
        div {
            height: 150px;
            width: 150px;
            border: 2px solid red;
        
            overflow: hidden;
            display: flex;
            align-items: center;
            justify-content: center;
        }
    </style>
</head>
<body>
    <div>
        <img src="sun.jpg" alt="">
    </div>
</body>
</html>
Dino Cajic
  • 138
  • 1
  • 7
6

i'm a huge fan of making an image the background of a div/node -- then you can just use the background-position: center attribute to center it regardless of screen size

Shain Lafazan
  • 323
  • 5
  • 6
  • 11
    This is not an accessible way of using images. It may work fine for decorative images, but any image which is informative needs alt text. – user2532030 Mar 23 '17 at 11:36
  • Also, anytime you use a background image, it will flash annoyingly the default background color until the background image is completely loaded, and there is no way to preload. – David Spector Apr 16 '21 at 22:10
5

Do not use fixed or an explicit width or height to the image tag. Instead, code it:

      max-width:100%;
      max-height:100%;

ex: http://jsfiddle.net/xwrvxser/1/

  • 1
    This then leaves space around the image which I believe they are trying to avoid. – Eoin Dec 12 '17 at 17:03
1

I found this to be a more elegant solution, without flex:

.wrapper {
    overflow: hidden;
}
.wrapper img {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    /* height: 100%; */ /* optional */
}
Cezar D.
  • 356
  • 1
  • 6
  • 12
1

One option you can use is object-fit: cover it behaves a bit like background-size: cover. More on object-fit.

ignore all the JS below, that's just for the demo.

The key here is that you need to set the image inside a wrapper and give it the following properties.

.wrapper img {
  height: 100%;
  width: 100%;
  object-fit: cover;
}

I've created a demo below where you change the height / width of the wrapper and see in play. The image will always be vertically and horizontally centered. It will take up 100% of its parent width and height, and will not be stretched / squashed. This means that the aspect ratio of the image is maintained. The changes are applied by zooming in / out instead.

The only downside to object-fit is that it doesn't work on IE11.

// for demo only
const wrapper = document.querySelector('.wrapper');
const button = document.querySelector('button');
const widthInput = document.querySelector('.width-input');
const heightInput = document.querySelector('.height-input');


const resizeWrapper = () => {
  wrapper.style.width = widthInput.value + "px";
  wrapper.style.height = heightInput.value + "px";
}

button.addEventListener("click", resizeWrapper);
.wrapper {
  overflow: hidden;
  max-width: 100%;
  margin-bottom: 2em;
  border: 1px solid red;
}

.wrapper img {
  display: block;
  height: 100%;
  width: 100%;
  object-fit: cover;
}
<div class="wrapper">
  <img src="https://i.imgur.com/DrzMS8i.png">
</div>


<!-- demo only -->
<lable for="width">
  Width: <input name="width" class='width-input'>
</lable>
<lable for="height">
  height: <input name="height" class='height-input'>
</lable>
<button>change size!</button>
volt
  • 963
  • 8
  • 17
1

Building on @yunzen's great answer:

I'm guessing many people searching for this topic are trying use a large image as a "hero" background image, for example on a homepage. In this case, they would often want text to appear over the image and to have it scale down well on mobile devices.

Here is the perfect CSS for such a background image (use it on the <img> tag):

/* Set left edge of inner element to 50% of the parent element */
margin-left: 50%;

/* Move to the left by 50% of own width */
transform: translateX(-50%);

/* Scale image...(101% - instead of 100% - avoids possible 1px white border on left of image due to rounding error */
width: 101%;

/* ...but don't scale it too small on mobile devices - adjust this as needed */
min-width: 1086px;

/* allow content below image to appear on top of image */
position: absolute;
z-index: -1;

/* OPTIONAL - try with/without based on your needs */
top: 0;

/* OPTIONAL - use if your outer element containing the img has "text-align: center" */
left: 0;
Dave Koo
  • 463
  • 4
  • 12
0

The width and height are only for example:

parentDiv{
    width: 100px;
    height: 100px;
    position:relative; 
}
innerDiv{
    width: 200px;
    height: 200px;
    position:absolute; 
    margin: auto;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
}

It has to work for you if the left and top of your parent div are not the very top and left of the window of your screen. It works for me.

  • Didn't seem to make a difference. Not sure if it has anything to due with the fact that the width is fluid, while the height is fixed on what would be the parent div. – Tom Jan 28 '13 at 13:28
  • 2
    This technique works if the image is smaller than the container. – otherDewi Oct 12 '13 at 04:30
0

based on @Guffa answer
because I lost more than 2 hours to center a very wide image,
for me with a image dimendion of 2500x100px and viewport 1600x1200 or Full HD 1900x1200 works centered like that:

 .imageContainer {
  height: 100px;
  overflow: hidden;
  position: relative;
 }
 .imageCenter {
  width: auto;
  position: absolute;
  left: -10%;
  top: 0;
  margin-left: -500px;
 }
.imageCenter img {
 display: block;
 margin: 0 auto;
 }

I Hope this helps others to finish faster the task :)

CristiC777
  • 481
  • 11
  • 20