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:

  1. <div class="floatclear">
  2. <p><img src="image.jpg" alt="" />
  3. Ye olde body text</p>
  4. </div>
  5. <div id="byline">
  6. Ye olde document byline
  7. </div>

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:

  1. .floatclear {
  2. display:inline-block;
  3. width:100%;
  4. }
  5. /* Hide from IE-mac \*/
  6. * html .floatclear {
  7. height:1%;
  8. width:auto;
  9. }
  10. .floatclear {
  11. display:table;
  12. }
  13. /* 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

Comments

  1. Gosh! That’s some clever stuff. Seems to work fine in WinIE5.x.

  2. December 14, 2004 by Mordechai Peller

    Interesting, but why not: #byline { float : left; width : 100% }

  3. I recently ran into this, and after looking at it for a minute, I decided “I liked it” — so I have never bothered to fix this problem.

    http://www.hiram.net/km36/

    I have also noticed that this does not show up in IE6 — but it does in Firefox and Camino.

    Gee, and I thought it was a “feature” … LOL

  4. December 14, 2004 by Roger Johansson (Author comment)

    Mordechai: That would work in this specific case, but I wanted a solution that doesn’t involve having to float everything. Anyway, the more ways of solving layout problems there are, the better :).

  5. What if you placed a br with a style of “clear: right” after the image but before the div is closed? Of course, this would only work if you aren’t floating some other page element in the same direction as the image and in the same vertical space.

    <div> <img src=”#” /> <p>blah blah blah blah</p> <br /> </div>

    div img { float: right; } div br { clear: right; }

  6. December 14, 2004 by Mordechai Peller

    Roger:

    That would work in this specific case,

    While I think it will work in more than “this specific case,”

    the more ways of solving layout problems there are, the better :).

    …you are ultimately correct in that the more tools we have at out disposal, probably the better.

    I wanted a solution that doesn’t involve having to float everything.

    Why not? It keeps the pages lite. :D

  7. For me it’s standard procedure with something that has clear:both at the bottom of blog entries, since I always float images in the entry. My last design just had
    , but I never really did bother to check how that worked with less competent browsers.

  8. Nice, but spoiler is the need to use another div… But sometimes it could get handy.

  9. December 15, 2004 by Roger Johansson (Author comment)

    Graham: In this case, that would have the same effect as clear:right on the byline div - it would push the byline below the right sidebar.

    (I cleaned up your comment a bit. Sorry for the preview form eating your entities.)

    Mordechai: ;-D

    dusoft: Yeah I agree. Sometimes you may be able to apply this to an existing div with no need to add an extra one. And I guess you could argue that a div containing the body text is structural.

  10. I have spent ages looking at this issue so thank you very much Roger! I have just implemented it on my site and it works great. I added the class to the p tag (rather than a div) and then added some more styling for the image with: .floatclear img {…}

  11. Roger,

    this is awesome. I was asking me the question recently, since most float articles seem to avoid this special point of “How much should clear clear?”. Interesting enough, this problem doesn’t show in Mozilla 1.7 and Firefox 1.0PR, but it does show in FF 1.0.

  12. December 17, 2004 by Roger Johansson (Author comment)

    Minz: Yes, Mozilla and Firefox were recently updated to display the correct behaviour with regards to clearing floats. A clear now clears all previous floats.

  13. Hello,

    Seems like this hack doesnt validate W3C CSS, at least not when I tried: http://jigsaw.w3.org/css-validator/validator?uri=http%3A%2F%2Fwww.456bereastreet.com%2Flab%2Fclearingfloats%2Fdisplaytable.html&usermedium=all

    Does anyone know how to solve this?

    Thanks!

  14. December 17, 2004 by Roger Johansson (Author comment)

    Stefan: If you’re referring to display:inline-block giving an error, it’s because the CSS validator needs updating to support the CSS 2.1 spec.

  15. Roger, thanks.. that makes sense. The warning was about the inline-block, but now I know why..

  16. I encountered this problem recently too but I seem to recall (at the moment, I can’t access the CSS) that I used min-height which was set to the height of the graphic. In this page of mine, it is the faculty directory in which the faculty photos are the same size so <div class=”facultymember”>…</div> could use the same style rule. I can see that other circumstances may not use this method as well.

  17. Seems like the image needs to be inside of an <p>-tag, otherwise the image will be invisible in some cases using Firefox 1.0. A little bit strange :(

  18. Great stuff!!…and much better than the PIE solution. But you don’t really need the Holly hack, the width:100% has the same effect. Like this:

    .floatclear { display:table; width:100%; } /Mac IE only */// .floatclear {display:inline-block;} /End Mac IE only/

  19. That last CSS rule in comment 18 was supposed to be inside the “Mac bandpass filter”

  20. I’m told that “inline-table;” does the same fix on IE/Mac as “inline-block;”, while not throwing a validation error. Some people are complaining because their “W3C” buttons are returning their sites as “invalid”, so this alternative will prevent that. BTW, I just found out about the “display: table;” trick. I agree that is a much cleaner way then all that :after cruft. Why does a finished technique in CSS always have to go thru so many gyrations before the simple easy way is discovered?! Oy.

  21. January 15, 2005 by Roger Johansson (Author comment)

    Erik: Ah. Yeah that would make it even cleaner. Thanks.

    Big John: I’ll take a look at that alternative IE/Mac fix.

  22. RE: inline-block validation error.

    If you wrap the inline-block rule in the IE5/Mac Band Pass Filter the validator won’t see it

  23. January 31, 2005 by Mikael Ahlinder

    FYI, since the solution above didn’t do the trick for me when using multiple floated sub-divs (they got stacked under eachother) i tried this solution. And it worked ;)

  24. January 31, 2005 by Mikael Ahlinder

    addition to #23: that stacking behaviour showed up in FireFox…

  25. January 31, 2005 by Roger Johansson (Author comment)

    Mikael: That’s interesting. I’ll have to look into that.

    Oh, and if you read my article closely, you’ll notice that it contains a link to the Easy Clearing article at Position Is Everything ;-)

  26. February 1, 2005 by Mikael Ahlinder

    oh, i’m sorry =/

  27. Good!

  28. What about min-height on the text?

  29. Roger, you’re solution worked well for me. Thanks.

  30. I went back and worked on getting the css to validate. I had to combine the original instructions with several of the comments, but here’s what I came up with, if anyone is interested:

    .bText {display:table; width: 100%;}

    /*///

    .bText {display:inline-table;}

    /**/

    That seems to work and validate.

  31. Shoot, I forgot to replaces the .bText with .floatclear. Let’s try that again:

    .floatclear {display:table; width: 100%;}

    /*///

    .floatclear {display:inline-table;}

    /**/

  32. I have been strugling with this problem for some time but I found the easy and straightforward solution here. Thanks a lot!

  33. hi thanks for this, been bugging me for a few months. But in your example pages - how would you set the css so that the content section could be above the left and right sidebars in the code?

    tom

  34. OMG if that workaround was a hot girl i would make love to it! 2:08am and i can go to bed now

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.