After: float

by Kyle Simpson

Continuing in my series of “why is this so hard in CSS or HTML?” type articles, here I want to discuss the process of dealing with floated elements.

Before I get started, let me just disclaim: I’m not talking here about using CSS float as an approximation for layout structure, things like columns. In this other article, I cover what the differences are, IMO, between layout and positioning.

With that distinction in my mind, I will be talking here about positioning using floats, floating an element (left or right), with other inline content (text, images) flowing around it. This is the original (and I think still most valid) intended-usage of float. If you are trying to do things like columns, I think you should avoid float and instead use flexbox. (And no, I don’t think you should use flexbox but then include float as legacy-browser fallbacks. I think you should make a clean break, use only flexbox, and just let your “layout” break in older browsers.)

But we’re not talking about layout structuring here. We’re talking about positioning an element to the left or the right of other inline content. That is what float is best for.

What’s the floating problem?

Put simply, if you want to take an element and float it around some other content, that floated element no longer takes up any vertical space, so if it happens to be bigger than the container would otherwise be sized by its other content, then it overflows, rather than stretching the parent.

Example:
 
<style>
#parent { width:200px; background-color:yellow; padding:5px; }
#child { float:right; width:75px; background-color:gray; color:white; height:100px; padding:5px; }
</style>
 
<div id="parent">
   <div id="child">Very cool!</div>
   Hello, this is some text
</div>
 

 

As you can “clear”ly see, the yellow box is not being stretched vertically to encompass the size of the gray box.

 

The “fix”

It turns out that CSS has something of a “fix”, although I would probably define this as more of a “hack”.

The clearCSS rule “specifies whether an element can be next to floating elements that precede it or must be moved down (cleared) below them.”

clear can take one of these values:none, left, right, or both (or the default: inherit). none and inherit are self-explanatory: no clearing (unless otherwise instructed). left and right make sure that the element “clears” (sits below) elements that are floated to the left and right of the container, respectively. both obviously makes sure an element clears both float “areas” (if present).

So, the hack ”fix” is to insert an element into the container, at the end, which has no content, but which is instructed that it must “clear” any floats on either side of the container, thereby forcing the element to the bottom, and also ensuring that the container is stretched to encompass the floated elements.

The markup way

Just insert a dummy HTML element and clear:both it. No big deal, right? :)

   
<style>
#parent { width:200px; background-color:yellow; padding:5px; }
#child { float:right; width:75px; background-color:gray; color:white; height:100px; padding:5px; }
.clearfix { display:block; clear:both; width:0px; height:0px; }
</style>
 
<div id="parent">
   <div id="child">Very cool!</div>
   Hello, this is some text
   <span class="clearfix"></span>
</div>
 

 

Here, notice I put in a <span> which I styled with a class “clearfix”. The element has to be a block-level element (hence display:block) and we put clear:both on it. For good measure, I make sure it’s 0×0 dimensions.

Obviously, this solution is less ideal because we clutter up our markup. But it has the slight “advantage” (if you call it that) that it works on older legacy browsers. Can we make it a little less intrusive?

The CSS way

CSS3 gave us pseudo-selectors, so namely the:after (actually, more correctly, ::after) selector will let us insert our phantom hack/fix element via CSS instead of cluttering up our markup. This is actually a decent usage of the ::after feature as it’s adding something (some “content”) purely for presentational purposes.

<style>
#parent { width:200px; background-color:yellow; padding:5px; }
   #parent::after { content:"."; display:block; height:0px; clear:both; visibility:hidden; }
#child { float:right; width:75px; background-color:gray; color:white; height:100px; padding:5px; }
</style>
 
<div id="parent">
   <div id="child">Very cool!</div>
   Hello, this is some text
</div>
 

 

Notice something interesting about the semantics of::after. We place it on the parent container, not on the children, and it doesn’t mean the element comes strictly after the parent, it means it comes as the last child-element of the parent container. Can be very confusing until you recognize that detail.

But nevertheless, we insert our phantom element, and all’s well.

Note: There are lots of variations of the styles you apply to your “clearfix”, for handling various weirdnesses in various browsers and situations. I won’t cover any of them in detail, but one popular variation is called micro clearfix. That uses another “hack” of table-CSS display to prevent certain margin collapsing absurdity. Yay.


Really?

I bet you know what I’m about to say, if you’ve read my other CSS articles.

Is this really the best we can do? I mean, when you think about it, we’re just “hacking” the float system a little bit, to force there to be a non-floated element (that we don’t care about) in our parent container, one that the parent container can’t ignore.

This phantom element has its position controlled by the clear behavior, but the parent really doesn’t know or care about that, it only knows that it needs to stretch around that phantom element. Kinda bizarre and indirect when you think about it.

Could we do better? I would hope so.

Here’s an idea, a soft-proposal if you will:

   
<style>
#parent { float-cover:both; width:200px; background-color:yellow; padding:5px; }
#child { float:right; width:75px; background-color:gray; color:white; height:100px; padding:5px; }
</style>
 
<div id="parent">
   <div id="child">Very cool!</div>
   Hello, this is some text
</div>
 

What if we tell the parent, “hey, you should cover all the floated elements in your own dimensions”. In other words, the parent should always  clear:both on the end of itself (instead of needing a phantom element for that clearing).

float-cover would take the same values as clear does, and it would mean basically the same things. left would cover the left-floated elements for dimensions, right would cover the right-floated ones, etc. The default would of course be none, but with a simple both, you get perfectly cleared floats.

FTW? I think so. But hey, I’m just a JS nerd. What do I know?

Tell us what you think? How can we best make this a standard and remove the hack!?

For more complete information about compiler optimizations, see our Optimization Notice.