IE Bug Improperly Displays Floats

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:

Flowing red and blue boxes

Now, let’s add float: left; to #red and see what happens in Firefox:

Floating red with blue, 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:

Floating red with blue, IE

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 ps, 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.