CSS and DIV layout: The main pitfalls
In the beginning web pages were simple and innocent. Then people started to abuse tables to achieve some layout. And then came CSS and DIV, the winning combination for making a web page oh-so-beautiful but not resizable. Web designers started telling the browser where to put what, and the browser responded with blind obedience, even if the result was a rubble of text and graphics. That’s the deal: Use absolute positioning to get full control, but lose the browser’s capability of avoiding nasty accidents.
So here is my short list of the main problems you may run into, while attempting to layout a web page using CSS-backed DIVs. If you really have to, that is.
Pitfall #0: Resize
If your page is made with CSS, don’t let the width change. Period. Play with CSS/DIV layout only when you’re inside a box, whose width is known and fixed to the pixel. This is not very browser tolerant, I know. It may not work well on a tiny screen. Or a very big one.
But trust me on this: If you don’t set the overall width, you will sooner or later find yourself with spillovers and overlapping graphics. Maybe you can get it to survive a page resize on your browser. You have no chance with the IE version still not out.
If you want to allow a resize, use tables.
Pitfall #1: Huge CSS file (reference by ID)
This one is so common, that sometimes I wonder if I missed something.
Anyhow, the original idea of CSS, if I got it right, was to define the format in one place and put the content in another. I suppose someone hoped that people would go on using simple tags such as <h1> and <p> and get beautiful pages with clean HTML.
And to make things nicer, they allowed classes. So if you wanted a special paragraph, you just went ‘p.special’ in the style definition, and then <p class=”special”> in the HTML, and there you were, the HTML still readable and the format consistent and depending on the content.
But then came style-by-ID, which turned everything into a joke. This made it possible to define the style for a very certain element, pin-pointing it by its ID tag. So if I wanted to format one single paragraph, I’d go ‘#theID’ in the CSS file, and then <p id=”theID”> in the HTML. And that makes me wonder: If the styling is going to be used once anyhow, why not put it directly in the HTML? Why not go <p style=”…”> instead? Don’t tell me that you separate formatting from content, when you can’t change anything in the CSS file without checking the specific place where it’s used. If you remember where it was, that is.
Pitfall #2: The IE ‘width’ bug
The CSS2 spec paragraph 10.2 clearly defines the ‘width’ property as “that of the rendered content within”. Microsoft probably said “we are the standard” and decided to include the padding and the border in the ‘width’ property. This is said to be fixed in IE6, so maybe it isn’t an issue today, after all.
Below is drawing of the box model, and the difference between the two definitions of ‘width’. The sad bottom line is that if you need a consistent width of some element, padding and border have to be zero. If this is not an option, the only safe solution is to put your element in another element (a DIV, I suppose) and define the ‘width’ for the enclosing element.
Pitfall #3: Getting absolute positioning wrong
So let’s go through a few quick facts:
- Absolute positioning puts your element where it would anyhow, unless you specify otherwise for the X axis (with “left” and “right” style properties) and/or the Y axis (with “top” or “bottom” style properties). The only difference is that no space is allocated on the rendered page. The responsibility to keep the underlying area clean (or intentionally overlapping) is all yours. Usually, if you don’t know exactly what you’re doing, things will get wrong sooner or later.
- If you want the element on a fixed position on the screen, no matter how the page is scrolled (logo?), used the “fixed” position.
- “relative” position is not what it appears to be. It will move the block from its original position, but allocate space for it, as if it was there. Makes sense if you want chemistry-style subscripting, but otherwise I can see only one use: The zero-offset relative positioning, which is a way to set the ‘position’ property to something else than static (the default) without actually doing anything, and hence create a “containing block” in its natural place (more about that next).
- The “left”, “right”, “top” and “bottom” properties are related to the “containing block”, whose definition is a pitfall in itself. (See the CSS2 spec, paragraph 10.1, item 4). To make a long story short, the browser will climb up the hierarchy of blocks until it finds one, which isn’t statically positioned. It will then respect that block’s padding, and there is the zero point.
Now, “statically positioned” is the default setting of the “position” property, which means that the browser should put the element following the “old-fashioned” rules. See the example below for how to use the “relative” position to make the enclosing DIV effective.
If the containing block isn’t explicitly defined, hell breaks loose. The browser will most likely pick the root block, which is the top left corner of the page. Or not, depending on other junk you have on your page. Everything happens.
Pitfall #4: Absolute positioning know-it-all
I think that the main reason why “professional” web designers started talking about replacing <TABLE> formatting with <DIV> and CSS was that they first painted their web page with some graphics tool, and then wanted to copy their graphic design exactly into the web page. Which is a nice concept, if you can assure that the page will be viewed exactly like you expect: The same fonts, the same sizes, the same screen size, and so on. Which you can’t, but that’s another story.
No wonder they hated tables. It’s really a headache to control how the browser displays tables down to the pixel. Tables were, after all, not meant to be used that way. Getting the graphics from one table cell aligned with the next one is not easy. Not to mention more complex relations between graphic elements. And then came CSS and the DIVs.
The real power with DIVs is absolute positioning. Just make yourself a box, decide how large it will be (down to the pixel) and put whatever you want inside, exactly where you want it. Something like this:
<div style="position: relative; height: 200px; width: 200px; border: dotted red 1px;"> This is my canvas! <div style="position: absolute; top: 60px; left: 20px">And</div> <div style="position: absolute; top: 50px; left: 70px">I</div> <div style="position: absolute; top: 55px; left: 90px">can</div> <div style="position: absolute; top: 60px; left: 130px">put</div> <div style="position: absolute; top: 110px; left: 40px">the</div> <div style="position: absolute; top: 105px; left: 70px">elements</div> <div style="position: absolute; top: 112px; left: 140px">where</div> <div style="position: absolute; top: 150px; left: 80px">I</div> <div style="position: absolute; top: 155px; left: 90px">want!</div> </div>
And you get this:
Now I’m asking, isn’t this heaven? I say: It is, on your browser, on your computer. But since you’ve told the browser exactly what to do, you’ve also crippled its ability to fix things when the page is shown differently from how you expect it. A simple example follows.
Pitfall #5: Overflow
The page got resized? The fonts are shown larger than you defined? There was more text than expected? In the old days, the browser used to fix this. With CSS and DIVs you get rubbish.
The CSS spec says, that any block can overflow. What to do with the excessive graphic pulp is up to the browser (or it can be defined) but no matter how it’s handled, the result is ugly.
Let’s take a silly menu layout for example:
<div style="position: relative; width: 120px; height: 70px; border: solid 1px black"> <div style="position: absolute; top: 10px; left: 20px; background-color: #f8f;">Take me!</div> <div style="position: absolute; top: 40px; left: 20px; background-color: #ff8;">Or take me!</div> </div>
That looks like this:
The truth is, I can’t be sure that you see this as I do. But I’ll assume that it looks idiotic, but OK. But what happens if a script puts more text than expected? Or if the user forces a larger font? I’ll try to simulate this by choosing a larger font here:
But the truth is that I can’t know if this example went through OK, because I don’t know how your browser responded. But that’s the point, isn’t it?
If you want to be on the safe side, use the ‘overflow’ property. Below is shown what happens when you set ‘overflow: hidden;’ (to the left) or ‘overflow: scroll;’ (to the right) for the toplevel <div>. At least nothing spills over.
Pitfall #6: Floats
For some reason, which is beyond me, floats are commonly suggested as a solution for organizing web pages (except for horizontal layout, as shown below). There is only one problem with floats: They were intended to push text, and nothing else.
The whole idea was to allow an image to float to the left or right, and let the text surround it, just like as they do in newspapers. This is why the CSS2 spec says, in paragraph 9.4.1:
In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. (…) In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s content area may shrink due to the floats).
Let me spell this out: Paragraphs, DIVs or whatever, shouldn’t get pushed aside by floats. What should get pushed is the text they contain (or more precisely, inline elements). What happens in reality seems to depend on the browser. Firefox and Google Chrome follow the spec, IE plays its own game.
So let’s look at the following example:
<div style="border: 1px solid black; margin: 6px; padding: 5px; float: left; width: 4em; height: 4em; background-color: #ffdddd; text-align: center;"> Nice image</div> <div style="background-color: #ff8; width: 12em; border: solid 1px black;"> This is my DIV, which has melted into the float. Only the text is pushed aside. </div>
Which looks like this on your browser:
Note that the non-floating DIV (in yellow) should surround the float. It should be packed as if the float wasn’t there. This is what the spec says should happen. In real life I got:
Not very impressive, is it?
By the way, if you want a rectangle which doesn’t mix with floats, put a table there. Tables are inline elements, so they won’t collide with floats. But if you’re playing with floats, you must be thinking that tables are obsolete…
Alternatively, you could go for a floats-only region. People who test with IE only are likely to get this wrong, because of the problem mentioned above. Anyhow, the idea is to pack DIVs horizontally to the right and to the left using floats, and avoid collision from top and bottom using the “clear” directive. For example:
<div style="background-color: #ffd; border: solid 1px black; text-align: center;">Above all</div> <div style="float: left; background-color: #fdd; width: 4em; height: 3em; border: solid 1px black; text-align: center;"> One</div> <div style="float: left; background-color: #fdd; width: 4em; height: 2em; border: solid 1px black; text-align: center;"> Two</div> <div style="float: right; background-color: #fdd; width: 4em; height: 4em; border: solid 1px black; text-align: center;"> Four</div> <div style="float: right; background-color: #fdd; width: 4em; height: 3em; border: solid 1px black; text-align: center;"> Three</div> <div style="clear: both; background-color: #ffd; border: solid 1px black; text-align: center;">And this is below all!</div>
Which looks like this:
This is great for multiple columns, whose heights are unknown. We want the bottom DIV below all of them, and this is how it’s done. Note that if we put a non-floating DIV in the middle, we would get different behavior on IE and W3C-compliant browsers.
Note that none of the DIVs in this example contains the other. What makes them avoid each other is the “clear: both” attribute at the bottom which tells the browser that floats are not allowed in either side. So we get a clean line, immediately below.
One problem (or feature) of this method, is that if there is not enough horizontal space, floats will be pushed vertically, a bit like inline flow. Paragraph 9.5 of the W3C specification says:
If there isn’t enough horizontal room on the current line for the float, it is shifted downward, line by line, until a line has room for it.
This can’t be relied on too much, though. It looks like Firefox prefers to let some of the page go out of view rather than to mess the page up, when the page is resized to very narrow (below 400 pixels?). I don’t know exactly how it works, but the fact is that at some point the floats don’t tile vertically (in IE they do). I guess it’s an interpretation of “not enough horizontal space”.
And just a final remark about floats: The W3C specification requires that the width is known, and must therefore be set unless it has an intrinsic value (e.g. an image). But nothing stops us from setting the width in percents. With this simple trick, we can have columns that grow and shrink according to the limiting DIVs width (resizing…), while controlling the proportions between the columns.
Just a warning, though: Be careful with letting the overall width come near 100%, since the browser may round each float’s pixel width slightly. And of course, keep in mind that the width doesn’t include the border, margins nor padding, so these can cause a mess when things get tight. Not to mention mixing percentage width floats with constant width floats.
Pitfall #7: Getting the selection wrong
The grammar is in principle simple, but knowing the formalities is important. For example, div.theclass { } means a DIV with class “theclass”, but not necessarily a DIV within it. For a DIV within a certain class, the selection is e.g. “.theclass DIV { }”. This also goes for IDs, e.g. “#theID DIV { }”. The list can be nested, so a link within a list item within a certain ID could look like “#theID li a:visited { }”. The same trick goes with classes instead of IDs.
If several cases are desired, start the nesting from the beginning. Exactly like “a.banner:link, a.banner:visited, a.banner:active { }” one should also go “banner a:link, banner a:visited, banner a:active { }” when a “within” relation is desired rather than a direct one.
Conclusion
If you want to control your page’s layout down to the pixel, DIVs and CSS are your screws and screwdriver. Just remember that things may look very different when someone views your page with a cellular phone or some other gadget you wouldn’t think about.
As they always say, when everything else fails, RTFM (Read the Fine Manual). In the case of CSS, that manual is the W3C specification. This is not to say that browsers really follow it. Firefox does most of the time. At least they consider it a bug when it doesn’t. Microsoft and their IE live in a world of their own.
Anyhow, if you wonder why your <div><div><div>-page is a mess, or why everything goes wrong with patches of page fragments being where they shouldn’t be, the ultimate answer is to understand how the fine machine works. Or at least, how it was meant to. And that is written in the spec. Boring, but a man has to do what a man has to do.
Reader Comments
Thank you for this. My page IS a mess because I did everything bad you wrote in this article. I like your matter-of-fact writing style. Suited me nicely. On my way to fix my crap. Thank you.
Sad that IE6 renders those things radically different, but I think with IE7 it mostly shows as you would expect (at least with floating/positioning issues).
And last I checked, according to gs.statcounter.com, IE6 is down to about 10% of users, the rest being IE7+, Firefox, Safari, Opera, Chrome.
I wonder if we should care about IE6 anymore – Google Doesn’t: (http://www.tgdaily.com/html_tmp/content-view-40785-140.html)
As for the issue with floats (pitfall #5), IE7 is still showing it wrong. Just checked with IE 7.0.573 under XP.
I would love to “drop support” for certain browsers, but when it comes to layout, it’s my site that’s going to look bad. It’s not like the browser puts a sign saying “Oops I just messed things up”.
Too bad…
“If the styling is going to be used once anyhow, why not put it directly in the HTML?”
Because of the “Cascading” part of CSS. It’s much easier for me to keep track of anomolies inside one CSS file than to check half a dozen pages where I might have an exception. Then I can easily see what part of the anomoly is different and what it shares with the parent style, so that if I redesign, I only have to correct one place and it changes automatically (plus these IDs can be reused across different pages). For example:
h1 { border: 1px solid blue; font-size: 15px; color: blue; }
h1#special { color: red; }
I do agree that ids are over-used (part of overall CSS divitis), but they can be very useful from a style-tracking pov.
Good article. I’m fairly new to web design and these points are all good and helpful to have a handle on. PS join the anti ie6 revolution – add this code to your web pages! “
Time to upgrade your browser
If you’re reading this, you’re surfing using Internet Explorer 6, a twelve-year-old browser that cannot cope with the demands of the modern internet. For the best web experience, we strongly recommend upgrading to Firefox, Opera, Safari, Google Chrome, or a more recent version of Internet Explorer.
“
If we are working with up to date browser of Mozilla, Opera or else except IE then no problem. All will be work fine.
And Programmer need to program/make website for other who are fond of IE also some are doesn’t know mozilla/opera exist or not!
But they wanna things which is up to date…Problem is you can’t play NFS Most Wanted in Pentium 2 PC.
VERY outdated, needs an update! That or removed, very misleading with today’s browsers.
Outdated? Most issues are directly related to the CSS standard, so they’re not expected to expire. And I mention versions in conjunction with specific browser-related problem. I trust the reader to judge for himself how relevant IE6 is at any time.