Updated (2009-01-15): I finally stumbled across the answer (see below).
[Originally posted 2008-01-18] I’ve recently come to understand a bug in how Internet Explorer handles floats. I doubt this is a new discovery, but I haven’t read about it and can’t easily find it through Google. To put it simply, floated boxes should overlap non-positioned block boxes but displace inline boxes; Internet Explorer displaces both. Now that I’ve said it simply, I’ll try to say it more clearly and accurately and with pictures.
We’ll start with a pretty simple XHTML document:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>IE Float Bug</title> </head> <body> <p id="red">red</p> <p id="blue">blue</p> </body> </html>
Now let’s add some color and play with our p
boxes, adding the following to our header:
<style type="text/css"> p { margin: 0 5px 0 0; padding: 0; color: #FFFFFF; border: 5px solid #000000; text-align: center; } #red { background-color: #FF0000; width: 100px; height: 100px; line-height: 100px; } #blue { background-color: #0000FF; width: 150px; height: 150px; line-height: 150px; } </style>
That should give us a page that looks like:
Now, let’s add float: left;
to #red
and see what happens in Firefox:
To explain what’s going on here, #red
is removed from the normal flow of the document. #blue
, then, being a block box, moves to where it would be if #red
were not in the document at all. The text inside of #blue
, though, has to be aware (if you’ll allow this personification) of the float, because its line box has to be shortened to avoid overlapping with #red
‘s margin box. This all conforms to the CSS 2.1 visual formatting model.
But look at what happens in Internet Explorer 7:
Instead of just displacing the text, the entire block-level box #blue
has shifted to make room for #red
. This would be entirely appropriate if I had created a new block formatting context by, for example, adding overflow: hidden
to #blue
. But so long as #blue
‘s overflow
is set to its default value of visible
, as it is here, we have no new block formatting context, so #red
should overlap #blue
.
So how do I fix this? How do I make IE render this the same way as Firefox? I would appreciate any help.
Update: The Answer
If you can avoid triggering hasLayout in IE, then #blue
will behave as it should. In the example above, setting height
and width
on #blue
both triggered it, but with a few changes, we can avoid that to get the results we want.
First, remove height
and width
from #blue
. To be honest, height
wasn’t necessary, since line-height already sets the height of the element (assuming you’ve only one line of text).
So how do we constrain the width? By setting width
on body
(or a div
that wraps both p
s, if you want to do this in the context of a real page). So give body
a width
of 165px
(to accommodate the desired width, margin, and border or #blue
).
So our final CSS looks like:
<style type="text/css"> body { width: 165px; } p { margin: 0 5px 0 0; padding: 0; color: #FFFFFF; border: 5px solid #000000; text-align: center; } #red { background-color: #FF0000; width: 100px; height: 100px; line-height: 100px; } #blue { background-color: #0000FF; line-height: 150px; } </style>
That gives us the same result in IE and Firefox.