One task css doesn’t make easy is centering objects vertically within their containers. Last summer I offered 6 methods for vertical centering to address that difficulty. The methods generally require knowing in advance the heights of the parent and child elements. What do you do when the child element’s height isn’t known?
A variation of this question was brought to my attention by Manuel who posed it in a comment on the vertical centering post mentioned above.
I´d like to ask you about vertical centering a child element with a variable height. This is the case, for example, when inside a parent div of fixed size I want to center vertically a child div with a variable number of rows of images, and sometimes there is 1 row, and other times it could be 2 or 3.
Vertical Centering with CSS
Here’s a quick reminder of the methods for centering. I’ll mention some of their specifics in this post, but for the full details on each see this post on vertical centering. You can click the methods listed below and be taken to the original demo for each.
- Line-Height Method
- CSS Table Method
- Positioning and Negative Margins
- Positioning and Stretching
- Equal Top and Bottom Paddings
- Floater Div Method
There’s an assumption with all of these methods that the height of the parent is known as is the height of each child element. What’s unknown is how many rows of child elements there will be.
Also if you have a better solution for any of these, please share. This was a quick look on my part to get things to work, but there could very well be better solutions.
CSS Table Methods
The reason I found 2 solutions using css tables is because I started by making things more complicated on myself than necessary. First the complicated solution and then the much simpler solution.
I started by wrapping each of the child elements in a container div that I set in css as a table-row. Then I added empty divs, also defined as table-rows, above and below the child elements and wrapper rows. The html is below with comments for how each div’s display property is set in the css.
The parent div is a table and the child elements are table-cells. The divs with a class of row are table-rows. Each also has additional properties set to make the display more presentable, though I removed this css from what you see below.
The empty rows are what do all the work here. The rows with the children have a defined height less than the parent’s height and what’s leftover is equally split between the two empty rows effectively centering what’s between them.
The Simpler Solution
The simpler solution is indeed much simpler. I don’t know why I missed it at first. Here’s the html, which you can see is much cleaner. It just wraps a container div around the elements to be centered.
And the css.
This is as simple as it gets. Where the original method sets the single child element as the table-cell and uses vertical-align to center it, here we treat the container as what’s being centered. We set it to a table-cell and vertically align it.
Best of all you don’t even need to set the height of the child elements. Regardless of what’s inside each child, the container will remain centered as long as the total height of the elements is less than the height set on the parent element.
Edit: Silly me. This isn’t as simple as it gets. As Gunnar points out in the comments, the container isn’t necessary. Here’s his solution, which now is as simple as it gets.
- Wrap the child elements with a container div
I’ll walk you through the positioning and negative margins method in detail and then point out where the other methods differ.
Positioning and Negative Margins
As a reminder the idea behind this method is to absolutely position the child element (the container) inside the parent. The top and left values of the child are then set to 50% each, which centers its top, left corner. Negative top and left margins are then used to pull the center of the child into position in the center of the parent.
The last step was to set the container’s margin-top and margin-left properties based on the values found above.
var container = document.getElementById(“container”);
var divWidth = container.offsetWidth;
var divHeight = container.offsetHeight;
container.style.marginTop = -divHeight / 2 + “px”;
container.style.marginLeft = -divWidth / 2 + “px”;
Positioning and Stretching
This method relies on setting top, right, bottom, and left values to 0, essentially stretching the child to all 4 sides. Width and height are then set before finally setting the margin on all 4 sides to auto. I used the same html as above.
Keep in mind this method doesn’t work in IE7 and below.
Equal Top and Bottom Padding
This method requires knowing the size of all elements and then calculating what padding (or margin) above and below the element would be needed to center it. Again the html here is the same.
padding-top = padding-bottom = (parentHeight – containerHeight) / 2
There’s also a box model got’cha in that once we add the padding we need to reduce the height value for the parent.
height = parentHeight – (parentHeight – containerHeight)
Alternatively you could set the top and bottom margin on the container, which wouldn’t require resetting the height of the parent. You could also set box-sizing: border-box and continue to use padding.
This last method floats a div equal to half the height of the parent, clears the child, and sets a negative bottom margin on the floated div equal to half the height of the child. The html has one extra div, the floater, which is added before the container.
< div id="parent">
< div id="floater">< /div>
< div id="container">
< div class="child">< /div>
< div class="child">< /div>
floater margin-bottom = -containerHeight / 2
Code and details for each method is again provided in the demo.
While vertical centering isn’t as easy in css as many of us might like, it’s not all that hard either. You can review my previous article to better understand several methods. Each expects you to know in advance the heights of all elements involved.
Sometimes you won’t have that information in advance. In the cases here the total height of the elements to be centered is unknown because we don’t know how many elements (or rows of elements) we’ll have.
The css table method didn’t care if we knew the combined height of the elements being centered. I was able to work out a simple and complicated solution using it.
Again I worked all these out quickly and there might be better solutions. Please share if you know of any. Also know that nothing above was tested exhaustively, though I feel comfortable saying the solutions should work in the same browsers that the general methods work.
Download a free sample from my book, Design Fundamentals.
I’ve created another tehnique that uses vertical-align. You can read more about it here: http://coderwall.com/p/oo2bqg
I like the vertical-align method, but why does it work?
Thanks Idered. Another good method. I think it works for the same reason the line-height method works since with the line-height method your inside container (the text) is an inline element.
Check that very old technic: http://www.flashjunior.ch/school/test/divcentering/index.cfm
There is a method called “ghost” element, it’s described in this page : http://css-tricks.com/centering-in-the-unknown/
The deal it’s to use the :before pseudo element.
And here is the demo page : http://jsfiddle.net/chriscoyier/hJtpF/
Very nice. I can’t believe I missed that one since I’m a regular reader of css-tricks. I wonder why it works. Is it the pseudo element or is it setting the child element as an inline-block?
Either way it seems like it (or the method Idered linked to above) might be the simplest way to do this.
I knew there had to be better ways than how I went about it. 🙂
I think the line-height/inline-block method can be useful if the width of the outer container is known. Just wrap the inner divs in a container, set the container to inline-block and tell the outer container its height with a line-height equal to that. The inner divs should be vertically centered, or am I missing something big?
I don’t think your missing anything. I skipped looking at line-height, since the original question didn’t specifically deal with text. That led me to be the one that missed the whole inline-block container thing.
You didn’t miss anything. I did. 🙂
Your simpler solution is not “as simple as it gets.” You don’t need the container element between parent and children elements, cf. http://dabblet.com/gist/3956210
Thanks Gunnar. I guess my way wasn’t as simple as it gets after all. Thanks for pointing out the container isn’t needed. I’ll edit the post to reflect that.
Here is another technique for modern browsers that doesn’t require setting the parent display, which can cause issues in some cases.
Thanks Robert. Looks like a nice easy method.