How To Vertically Center Unknown Rows Of Content Inside A Fixed Container

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.

My initial thought was Javascript would be needed to determine the total height of the child elements, but I thought why not take a look at each method and see what I could come up with. I created a demo with some explanation here and you can click on most of the images in this post to be taken directly to the specific part of the demo being discussed.

Longbridge Technology Park - Innovation Centre
Longbridge Technology Park — Innovation Centre

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.

I didn’t look at the line-height method, since it’s for centering a single line of text and Manuel’s question referred to images. I found 2 solutions using css tables, neither requiring Javascript. For the remaining 4 methods I did use Javascript.

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.

Please know I haven’t tested what I’m about to describe everywhere. They work in the latest versions of Safari, Chrome, Firefox, and Opera. I see no reason why they wouldn’t work elsewhere beyond what’s mentioned in the previous vertical centering post. All I’ve really added is some simple Javascript.

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.

2 child elements centered using css tables
CSS Table: Complicated method involving additional empty rows

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.

1
2
3
4
5
6
7
8
9
10
<div id="parent"> <!-- table-->
  <div class="row"></div> <!-- table-row-->
  <div class="row"> <!-- table-row-->
    <div class="child"></div><!-- table-cell-->
  </div>
  <div class="row"> <!-- table-row-->
    <div class="child"></div> <!-- table-cell-->
  </div>
  <div class="row"></div> <!-- table-row-->
</div>

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
#parent {
  height: 500px;
  display: table;
}
        
.row {
  display: table-row;
}
        
.child {
  height: 150px;
  display: table-cell;
}

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.

2 child elements centered using css tables
CSS Table: Simple method setting a container as a table cell

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.

1
2
3
4
5
6
<div id="parent">
  <div class="container">
    <div class="child"></div>
    <div class="child"></div>
  </div>
</div>

And the css.

1
2
3
4
5
6
7
8
9
#parent {
  height: 500px;
  display: table;
}

.container {
  display: table-cell;
  vertical-align: middle
}

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.

Javascript Required

For the remaining methods I used Javascript. The general idea is the same for each.

  • Wrap the child elements with a container div
  • Use Javascript to get the height of the container
  • Use Javascript to set the key css needed to make the method work

I’ll walk you through the positioning and negative margins method in detail and then point out where the other methods differ.

2 child elements centered vertically through positioning and negative margins
Positioning and Negative Margins

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.

Again the html is simple. I wrapped an additional container div around the child elements just like above. I’m using an id instead of a class for the container to make grabbing it in Javascript easy.

1
2
3
4
5
6
<div id="parent">
  <div id="container">
    <div class="child"></div>
    <div class="child"></div>
  </div>
</div>

The css below sets up everything with the exception of the negative margins. The parent is positioned relatively and the container is positioned absolutely with the 50% top and left values. Since we don’t know the size of the container we need to use a little Javascript.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#parent {
  width:800px;
  height: 500px;
  position: relative;
}
        
.child {
  width: 500px;
  height: 200px;
}
        
#container {
  position: absolute;
  top: 50%;
  left: 50%;
}

To get the height of the container, I created an onload function so the Javascript is run after the html has been rendered. Inside I created a container variable set to our container div. I used it to set new variables for the width and height of the container using offsetWidth and offsetHeight. Presumably the width would be known to us, but for this example I pretended it was unknown.

The last step was to set the container’s margin-top and margin-left properties based on the values found above.

1
2
3
4
5
6
7
8
9
10
onload=function() {
    
  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";

}

The rest of the methods work similarly. I’ll offer a quick reminder how each works and what the Javascript needs to do. If you’ve understood what I’ve done above you should be able to work out the details and again I’ve provided a demo where you can find all my code.

2 child elements centered vertically through positioning and stretching
Positioning and Stretching

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.

Here you use Javascript to get the height (and width) of the container as above and then set the css values for both. An oddity I discovered is that if you set all 4 top, right, bottom, and left values, the width and height returned in Javascript are the parent values. To correct this I only set top and left in the css and set right and bottom dynamically in Javascript after getting the width and height of the container.

Keep in mind this method doesn’t work in IE7 and below.

2 child elements vertically centered through equal top and bottom padding
Equal top and bottom padding (or margins)

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.

Once again you use Javascript to get the \height of the container and then use the value to set the padding on the parent. You need to grab the parent element in addition to the container and do a little math to set the padding.

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.

2 child elements vertically centered through use of a floater div
Floater div

Floater Div

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.

1
2
3
4
5
6
7
< div id="parent">
  < div id="floater">< /div>
  < div id="container">
    < div class="child">< /div>
    < div class="child">< /div>
  < /div>
< /div>

Once more you use Javascript to get the height of the container. Here we also need to grab the floater element in order to set it’s margin-bottom

floater margin-bottom = -containerHeight / 2

Code and details for each method is again provided in the demo.

Vertical Earth Kilometer
The Vertical Earth Kilometer

Summary

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.

The other methods require some simple Javascript to determine the total height (and width) of the child elements before using that information to set a bit of css.

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.

12 comments

    • Nice. I take it javascript is working behind the scenes to add the new text blocks. Are you doing something similar to what I did here to center the entire thing? Is javascript getting the height of the main container and then setting css?

    • 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. :)

  1. 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. :)

Leave a Reply

Your email address will not be published.

css.php