Wednesday, 19 January 2011

CSS for Developers: The Joy of Floats

As promised, the Long Awaited Follow-Up to CSS for Developers Part One! Well, long-awaited if you're as afflicted with NADD as I am.

Quick recap - the aim of this series is to provide a quick and easy how-to around the magic that is CSS.  It's primarily aimed at developers, although I would hope it's comprehensible to a range of tech-savvy people.

(Note: unless otherwise stated, the screenshots are in Chrome on a Mac - given an earlier version of this guide I can tell you the behaviour is identical to Firefox on Ubuntu).

Part Two: The Joy of Floats (or: why they don't do what you think they should)

Last time I stated that using float is a great way to position elements contained in a div.  Well, it has its downsides too.  Quite a few actually.

So this post documents the ones I ran into and what we did on the LMAX Trader UI to get it to look the way we wanted.

Disclaimer: There might be better ways to do a lot of this stuff - if you locate them via Google or work them out for yourself then well done!

2.1 Starting a New Line Using Floats

<html>
<head>
    <title>Horizontal flow</title>
    <style type="text/css">
        #left {
            background-color: cyan;
            width: 50%;
            float: left;
        }
        #center {
            background-color: yellow;
            width: 50%;
            float: left;
        }
        #right {
            background-color: red;
            width: 50%;
            float: left;
        }
        #next-line {
            background-color: green;
            width: 200px;
            clear: left;
            float: left;
        }
    </style>
</head>

<body>
<div id="container">
    <div id="left">Left</div>
    <div id="center">Center</div>
    <div id="right">Right</div>
    <div id="next-line">Next Line</div>
</div>
</body>
</html>
  • clear: left will reset any left float from before the div it is applied to. This will cause the div to be shown on a new line, although it can still have a float property as well
  • clear: right will put the div below any divs with a right float applied 
  • clear: both will do both. 

I usually do clear: both if I need to ensure the div will be on a new line.

2.2 Right Aligning Floated Divs

<html>
<head>
    <title>Float Right</title>
    <style type="text/css">
        #left {
            float: right;
            border: 1px solid gray;
        }
        #center {
            float: right;
            border: 1px solid gray;
        }
        #right {
            float: right;
            border: 1px solid gray;
        }
        #container {
            background-color: yellow;
            height: 26px;
        }
        #left2 {
            float: left;
            border: 1px solid gray;
        }
        #center2 {
            float: left;
            border: 1px solid gray;
        }
        #right2 {
            float: left;
            border: 1px solid gray;
        }
        #container2 {
            clear: both;
            float: right;
            background-color: #90ee90;
            height: 26px;
        }
    </style>
</head>

<body>
    <div id="container">
        <div id="left">Left</div>
        <div id="center">Center</div>
        <div id="right">Right</div>
    </div>
    <div id="container2">
        <div id="left2">Left</div>
        <div id="center2">Center</div>
        <div id="right2">Right</div>
    </div>
</body>
</html>
If you want to right-align a number of elements (for example, buttons at the bottom of a dialog), there are two approaches. One is to set float: right on each individual item (see the yellow panel above) - you'll notice that this means the divs are displayed in reverse order, because they're set to float to the right of each other. A better approach is to put the divs you want right-aligned into a container div and float that to the right (see the green panel). This ensures the correct ordering of the items.

2.3 Floating Behaviour to be Aware Of: float left next to float right (cross browser)

<html>
<head>
    <title>Float right next to float left</title>
    <style type="text/css">
        #left {
            background-color: blue;
            width: 50%;
            float: left;
        }
        #center {
            background-color: yellow;
            width: 50%;
            float: left;
        }
        #right {
            background-color: red;
            width: 50%;
            float: left;
        }
        #next-line {
            background-color: green;
            width: 50%;
            clear: left;
            float: left;
        }
        #float-left {
            clear: left;
            background-color: purple;
            float: left;
        }
        #float-right {
            background-color: cyan;
            float: right;
        }
    </style>
</head>

<body>
<div id="container">
    <div id="left">Left</div>
    <div id="center">Center</div>
    <div id="right">Right</div>

    <div id="next-line">Next Line</div>

    <div id="float-left">Floating Left</div>
    <div id="float-right">Floating Right</div>
</div>
</body>
</html>

In Firefox and Chrome (tested under Ubuntu and OS X), if you clear the float on a single div element, that div and all the ones after it will appear underneath all the previous ones.  For example, see the "Floating left" (purple) and "Floating right" (cyan) divs above.  The two divs appear on the same line, floating side-by-side.

This may even be the same behaviour in IE in strict mode (I don't remember if I tested it).  But by default, Internet Explorer renders this quite differently.


(Apologies for some of the inconsistency in screenshots - given my choices of operating systems, getting the cursed screenshots in IE was... challenging).

The point is that in IE the float: right is quite literal - it floats to the right directly underneath the last floated element, regardless of any prior clear commands.

If you want to get the same behaviour across all browsers, you need to be stricter with adding containers for different content areas:

<html>
<head>
    <title>Float right next to float left - correct</title>
    <style type="text/css">
        #left {
            background-color: blue;
            width: 50%;
            float: left;
        }
        #center {
            background-color: yellow;
            width: 50%;
            float: left;
        }
        #right {
            background-color: red;
            width: 50%;
            float: left;
        }
        #next-line {
            background-color: green;
            width: 50%;
            clear: left;
            float: left;
        }
        #float-left {
            background-color: purple;
            float: left;
        }
        #float-right {
            background-color: cyan;
            float: right;
        }
    </style>
</head>

<body>
<div id="container">
    <div id="left">Left</div>
    <div id="center">Center</div>
    <div id="right">Right</div>
    <div id="next-line">Next Line</div>
</div>

<div style="clear: both">
    <div id="float-left">Floating Left</div>
    <div id="float-right">Floating Right</div>
</div>
</body>
</html>

2.4 Floating Behaviour to be Aware Of: parent divs of floated elements do not resize to child

<html>
<head>
    <title>Floats in containers</title>
    <style type="text/css">
        #left {
            border: 1px solid blue;
            float: left;
        }
        #right {
            border: 1px solid red;
            float: right;
        }
        #container {
            border: 1px solid black;
        }
    </style>
</head>

<body>
<div id="container">
    <div id="left">Left</div>
    <div id="right">Right</div>
</div>
</body>
</html>
In the code above, we have a "parent" div, container, with two children, left and right.  Both of these children are floating, it doesn't actually matter that one's left and one's right.  What you might not be able to see clearly is that the parent, container, has zero height.  It's got a black border, you should be able to see that the top and bottom of the border are actually next to each other, showing the div has no height of its own.

However the children have a height, the height shrinks to fit the content, the text.

What I'm trying to get at is that if you want to place something underneath the left and right divs, you can't rely on the browser working out where the bottom is and placing other divs underneath them:

<html>
<head>
    <title>Floats in containers</title>
    <style type="text/css">
        #left {
            border: 1px solid blue;
            float: left;
        }
        #right {
            border: 1px solid red;
            float: right;
        }
        #container {
            border: 1px solid black;
        }
        #container2 {
            border: 1px solid yellow;
        }
        #left2 {
            background-color: green;
            float: left;
        }
        #right2 {
            background-color: purple;
            float: right;
        }
    </style>
</head>

<body>
<div id="container">
    <div id="left">Left</div>
    <div id="right">Right</div>
</div>
<div id="container2">
    <div id="left2">Left</div>
    <div id="right2">Right</div>
</div>
</body>
</html>
(You'll note this is not an article about design! I'm trying to use a combination of colours, outlines etc to make the point clear, not to make it look good)

Basically the floated divs are effectively absolutely positioned, and therefore take up no space on the document (more on this in a later post).

The way I've got round this in the past is to set a specific height on the parent div to force any following elements to appear underneath it. This works, but it can be fragile. In particular, it doesn't allow for wrapping text (more on this later too).

Still to come:
  • Position and Display: relative, absolute, block, inline... what does it really mean?
  • Horizontal and Vertical Centring
  • Strict Mode Is Your Friend
  • Column layout using CSS
  • Why "width: 100%" might go off the edge of the page
  • Cross-browser table border behaviour
Previously:

    1 comment:

    1. Really thank u for ur helping and this is really helped me a lot

      praneeth

      ReplyDelete