For many years, table-based web page layouts were the rule, not the exception. They were easy to build, they worked cross-browser, and WYSIWYG editors made it a breeze to create and edit tables. CSS-based layouts didn’t really grab hold until just a few years ago, thanks to the evangelism of people like Jeffrey Zeldman and Eric Meyer.
The benefits of CSS-based layouts are very well-documented, and include increased accessibility (for surfers who use assistive technology), ease of site updates (change an entire site’s look and feel with a single stylesheet), and improved search engine rankings (search engines are able to index your content more cleanly when it isn’t surrounded by table markup).
It’s getting easier and easier to avoid table-based layouts these days, and even WYSIWYG editors like Dreamweaver have shifted gears and started including CSS-based layout templates with their software. This is way cool.
But every now and then, a developer like myself will come up against something that was SOOOO easy with table-based layouts and winds up being a royal pain with CSS-based layouts. One of these “d’oh!” moments is when you try to vertically center an element on your web page. Umm… hang on, let me rephrase that: One of these “d’oh!” moments is when you try to vertically center an element on your web page when using Internet Explorer 6.
Firefox supports CSS standards better than Internet Explorer 6 (the dominant browser), and predictably, vertical centering in Firefox is a piece of cake!
This journal entry is devoted to explaining how to get vertical centering working in both Firefox and IE6.
First, an example page.
The ingredients
There are three keys to making this work:
- A set of nested DIVs (yes, I know some of you are anti-nested DIV, but hey the world keeps on spinning).
- CSS for standards-compliant browsers (Firefox et al)
- A separate chunk of CSS for our old friend IE6
Nested DIVs
The first requirement is nested DIVs. You may be thinking “what’s the point of avoiding tables if I still have bloated markup?” My response is that one small set of nested DIVs isn’t nearly as bloated as a table, and using DIVs still helps you keep your markup readable for accessibility purposes and search engines. It’s a good thing! DIVs are also much more flexible should you decide to change your layout later on.
Here’s the layout in all its glory:
<body>
<div id="outer">
<div id="container">
<div id="inner">
<img src="https://pipwerks.com/images/posts/indicator_green.gif" alt="Please wait" width="32" height="32" /><br />
Loading...<br/>
Well, not really.<br/>
But don't I look nice centered like this?
</div>
</div>
</div>
</body>
Code language: HTML, XML (xml)
The ‘inner’ DIV is what will hold your content. The ‘outer’ and ‘container’ DIVs’ sole purpose in life is to get your content centered on the page! I’ll explain this in a moment.
The CSS
Let’s look at the CSS:
body {
}
* {
margin: 0;
padding: 0;
}
/* macs won't see this! */
html, body {
height:100%;
width:100%;
}
/* END mac */
#outer {
height:100%;
width:100%;
display:table;
vertical-align:middle;
}
#container {
display:table-cell;
vertical-align:middle;
}
#inner {
text-align: center;
width: 50%;
margin-left:auto;
margin-right:auto;
}
Code language: CSS (css)
We set all elements on the page to have a default padding and margin of 0. This avoids box-model issues. We also dealt with some Mac inconsistencies with the Mac hack.
As you can see from the CSS, the body and outer elements (outer, container) are set to be 100% wide and 100% tall. The outer element is told to behave like a table (display:table), and is vertically centered using the “vertical-align:middle” property.
By nesting the ‘container’ DIV inside the faux-table, we can make the container DIV behave like a table cell: “display:table-cell”. This DIV is also set to be vertically centered using the “vertical-align:middle” property.
That’s it for the vertical centering in CSS-compliant browsers!
In this case, the ‘inner’ DIV’s CSS is purely for horizontal centering. I set the width to 50% simply because I wanted a narrow DIV, and I centered the DIV horizontally using margin-left: auto and margin-right: auto.
Remember, the code up to this point is for standards-compliant browsers, not IE6.
Dealing with our friend, Internet Explorer 6
Now, you may be thinking that writing different CSS specifically for IE harkens back to the bad old days of browser sniffers and alternate web sites, and you would be correct. However, the alternate CSS is very easy to manage, and doesn’t require a browser sniffer! Thanks to Microsoft’s implementation of conditional comments in IE, we can simply insert a chunk of alternate CSS code into a conditional comment… whatever is inside the comment will be safely ignored by any non-IE browser. Here’s an example:
<!--[if IE ]>
<style type="text/css">
body {
color: #ff0000;
}
</style>
<![endif]-->
Code language: HTML, XML (xml)
You can read more about conditional comments at Peter-Paul Koch’s website.
On our vertical centering example, I used the following CSS code in a conditional comment:
#container {
height: 1px; /* required for IE to properly center vertically */
position:relative;
top:50%
}
#inner {
position:relative;
top:-50%;
}
Code language: CSS (css)
Internet Explorer doesn’t support the “display: table”, “display: table-cell” and “vertical-align: middle” properties. To get around this, we have told IE to use relative positioning to move the ‘container’ DIV 50% down from the top of its parent DIV (‘outer’), and to move the ‘inner’ DIV negative 50% from the top its parent DIV (‘container’).
Normally these two values would simply offset each other and cause the ‘inner’ DIV’s content to be displayed at the top of the screen. However, through the magical glitches of IE, by setting the ‘container’ DIV’s height attribute, the DIV suddenly jumps down to the middle of the page, right where we want it to be! I have no idea why, but hey, it works. Here’s the proof.
Important: the height must be set BEFORE the top percentage is declared.
Externalizing the CSS
It’s always a good idea to set up your CSS in external CSS files. This helps keep your web page clean, enables you to re-use your code on other pages, and much more. The CSS contained in the conditional code can also be placed in an external stylesheet. Here’s how I set up my page:
<link href="centering.css" rel="stylesheet" type="text/css" />
<!--[if IE ]>
<link href="centering-IE.css" rel="stylesheet" type="text/css" />
<![endif]-->
Code language: HTML, XML (xml)
The CSS is exactly the same, but cut and pasted into two external files: centering.css and centering-IE.css.
Now my page is looking clean as a whistle, and I can reuse my code by linking my other pages to my ‘centering’ css files!
Cool, can you do that with a Flash SWF, too?
Yes, you can! Here’s an example.
The code for the Flash example is exactly the same as the first example, except for content: I replaced the contents of the ‘inner’ DIV with some dummy text, and I embedded a Flash SWF using Geoff Stearn’s SWFObject method (the best embedding method I’ve used by far!). No CSS was altered except for the body and font colors.
And there you have it… a nice, (relatively) simple way to vertically center DIVs using CSS and NO tables.
Updates
Thanks to some sharp-eyed readers, here are a few corrections and/or additions:
Update: Here is an example of a Flash SWF embedded with a transparent background. The CSS has been altered slightly: an HTML background image has been specified, and the inner DIV has been set to the exact pixel size of the Flash SWF; this properly centers the SWF and causes scrollbars to appear when a minimum width or height has been reached. Tested successfully in IE6, FF2 and Safari 3 (beta), all Windows XP.
Update 9/03/2007: For IE7, if specifying the height of the inner div, you must also specify the height of the container div in the IE-specific CSS file. For example:
In standard CSS:
#inner {
text-align: center;
width: 550px;
height: 400px;
margin-left:auto;
margin-right:auto;
}
Code language: CSS (css)
In IE7’s CSS:
#container {
/* required for IE to properly center vertically,
must match #inner height */
height: 400px;
position:relative;
top:50%
}
Code language: CSS (css)
Unfortunately, in IE7 this also produces scrollbars (IE7 thinks the page content is larger than it actually is). I plan to do some more research into this when I have the time.
Comments
niko wrote on August 30, 2007 at 6:27 am:
works fine expect what happens when your content height is about 500px..
Philip wrote on August 30, 2007 at 9:05 am:
the Flash example (transparent background version) uses a fixed size for the inner DIV, which is probably what you're looking for.
good catch, i should add an explanation for how to use a fixed size inner div.
Maxim Larivière wrote on September 3, 2007 at 7:02 am:
Hi, thx for the great example, but I think there is problems with the vertical centering in IE 7…
works fine expect what happens when your content height is about 500px..
the Flash example (transparent background version) uses a fixed size for the inner DIV, which is probably what you’re looking for.
good catch, i should add an explanation for how to use a fixed size inner div.
Hi, thx for the great example, but I think there is problems with the vertical centering in IE 7…