Clearing floated images in body text

A problem that I’ve been running into more often in these days of CSS based layouts than I did back in the old table days is the “image-sticking-out-of-the-bottom-of-the-content-area” problem that often occurs when an image is floated left or right, and there isn’t enough text to exceed the height of the image. A problem that sometimes had no easy fix.

This has been bothering me since there are occasions when you can’t just add more text or reduce the size of the image to circumvent the problem. Using a clearing element doesn’t always work either, so I started looking for a solution. After doing some thinking and experimenting I came up with a method that works well as far as I can tell. Not having seen it described anywhere else before, I thought I’d share.

But I never had this problem with tables

First some background on when and why this happens more often with CSS based layouts than with table based layouts. Table cells have a property that sometimes gets in the way, and sometimes is useful: they expand vertically to enclose their content, including any floated elements.

In table based layouts, most page blocks are contained in a separate table or table cell. As an example, assume you have an article that has a body text with some floated images, and a document byline with the name of the author and the publishing date. If you put the body text and the document byline in separate table cells, floated images in the body text won’t interfere with the byline, since they are in different table cells.

In CSS based layouts, on the other hand, page blocks are usually contained in div elements, which don’t contain floats the way table cells do. To illustrate the problem I made a basic three column layout by placing the sidebars before the content in the HTML, and then floating them to the left and right. If the body text and the document byline are each contained in a div element, any floats in the body text div may stick out of the bottom of it and intrude upon the byline div, which doesn’t look too good. [View example 1]

The fix that sometimes has problems

The easiest way of fixing this is to add a clear:both declaration to the byline div. Doing so will extend its top margin until the div pushed below any floated elements that preceed it in the document flow (for thorough explanations of that, I recommend reading Containing Floats by Eric Meyer and Float Layouts by Tommy Olsson). However, this may have an unwanted side effect, depending on how the layout is made. [View example 2]

In this example, the byline div is told to clear:both, which will make it clear both sidebars. You won’t notice this if the page’s content is taller than both sidebars, but if either sidebar is taller than the content area, there will be a gap between the end of the content and the byline. In some cases this may be fine, but it’s most likely not what you want.

I hope I’ve managed to explain the problem, and made you see why I wanted to find a better way of making sure floated images in the content don’t interfere with other parts of the page.

The workaround

I started thinking “what if you could make the content div behave like a table cell?” And then I realised that you can: by applying display:table to it. Only modern browsers understand that though, so as always, workarounds are needed for Internet Explorer, both Mac and Windows. After some tweaking, I found something that seems to work in all browsers I have tested in.

Using the same basic layout as in the previous examples, I wrapped the body text and the images it contains in an extra div:

<div class="floatclear">
	<p><img src="image.jpg" alt="" />
	Ye olde body text</p>
<div id="byline">
	Ye olde document byline

I got some ideas for the next step from the auto-clearing method described in How To Clear Floats Without Structural Markup at Position Is Everything.

The floatclear class is defined by the following CSS:

.floatclear {
/* Hide from IE-mac \*/
* html .floatclear {
.floatclear {
/* End hide from IE-mac */

This isn’t as complicated as it may look. First, display:inline-block is applied. This will magically convince IE/Mac to make the div contain any floats. The next declaration, width:100%, is needed to make modern browsers expand the div to the width of its parent container. More on why soon.

Then the Holly hack is used to exploit an IE/Win bug which makes it increase the height of the div to encompass any floats it contains. The width of the element is also reset to auto for IE/Win to prevent it from becoming too wide.

Finally, the div is set to display:table in the browsers that understand it. This is where the width:100% declaration comes in. Without it, the div would only be as wide as its content requires instead of expanding to the width of its container.

Done. No more escaping floats! [View example 3]

I’ve tested this in a whole bunch of browsers: Firefox 1.0, Mozilla 1.8a5, Safari 1.2.4, OmniWeb 5.1, Opera 7.5.4, IE 5/Mac, IE 6/Win, and Camino 0.8.2, and I can’t see any problems in either of them. I haven’t done any extensive testing with different kinds of content though, so if you find any problems or flaws in this method, please let me know.

Some final notes

You may not need to do any of the above if you use a different way to create a three column layout. For example, if you use negative margins, as described in Ryan Brill’s A List Apart article Creating Liquid Layouts with Negative Margins, you can use clear:right to push the byline below the content without making it jump all the way down to the end of the sidebars. That is, provided that the image is floated right. Float it left and clear:right won’t have any effect. Change it to clear:left and the byline will also clear the left sidebar.

Floating the whole content column could also work, though I’m not going into floats within floats within floats here.

The method I’ve described here is handy for cases where you, for one reason or another, can’t or won’t use either of the above layout methods. It will also clear any floats, regardless of which way they are floated.

Find any problems? Did I miss something obvious? Does this make any browser mess things up? Like I said, I haven’t tested this extensively, and there’s always room for improvement, so let me know!

Posted on December 14, 2004 in CSS