You must be really living out in the sticks if you haven’t heard by now that using HTML tables for layout is bad. Markup is for content, CSS is for presentation, we learned.
You must be living in the land of magic mushrooms if you haven’t realized by now that making CSS do exactly what you want is easier said than done.
See, in the ideal, happy-go-lucky world, everything is just right. The content, custom-tailored by suave professional markup writers, fits perfectly into the widths and proportions of your template. The browsers support standards at exactly the same level and in exactly the same way.
Too bad we don’t live in Mayberry. With the loony Czar “The Exasperating” CSS 1.50304 ruling the presentational realm, it is surprisingly hard to build templates that are not brittle. Even The Man himself is battling with the idiosyncrasies of the dominant Web browser. And he’s the sole content contributor to the site! What if the entries were written and posted by someone who neither knows nor cares about the quality of markup, which last time I checked, is the absolute majority of your typical content contributors?
The issue becomes even more complicated when the layout is fluid, because its exact sizes are not known at any given time. And when the business requirements call for more sophisticated visual concepts than your typical “two-column”, “three-column”, and “three-column with coke and fries”, it becomes clear that the old Czar alone may not be able to hold the kingdom together.
The “Stretchy Pants” test
One of my favorite TV characters, Frank Barone of “Everybody Loves Raymond”, was ecstatic to finally find a pair of slacks that fit just right his lesser than strapping figure. Nevermind that his wife Marie had secretly purchased them at a maternity store — Frank loved his “stretchy pants”, in which his beer-drinking, cannelloni-devouring gut would never feel too tight. In a very similar manner, your layout must accommodate the fact that the content may sometimes grow a bit of cellulite here and there. Therefore, in order to be deemed ready for the real world, any page layout must withstand this simple test:
In each area of the layout that will have changing content, place markup whose presentation exceeds the width and/or height that is originally allocated for the area. Doing so must not cause the areas to:
- Rearrange and change (break) the layout
- Bleed one over another
- Hide content of other areas.
This condition should still hold true if the browser window is resized to any width or height.
The ones with the heightened usability sense would add that these changes must also not trigger the appearance of in-page scrollbars. Go ahead and play with the pure CSS-driven sites. Download the source code of their pages, test it. View these pages in IE, view them in Mozilla, maybe even check them out in Opera. You will see that hardly any of them pass the test. In fact, if you find one that passes it and has more than one column, let me know.
The secret that CSS gurus have been trying to hide from you
But guess what? The table-driven layouts (if designed properly) pass the “stretchy pants” test with flying colors. Perhaps nobody enunciated the issue better than Andy Budd and Dave Shea in their insurgent strike against us structural markup purists: tables are bad, but they may be necessary to keep the layout intact in real-life conditions. Olav Junker sums up the problem in his comments:
CSS is perfect for basically single-column layouts. Secondary blocks like sidebars can be placed outside the main column with float. This solves the layout requirements of many websites, but requires the designer to rethink the layout as column-based rather that grid-based. It’s not just a case of moving some attributes into a slightly different CSS syntax.
However, sometimes you really need blocks aligned in two dimensions. You can emulate it with positioning and fixed sizes, but this will not, like tables, adjust gracefully if content is changed.
So my point is, it’s not a case of CSS being harder to learn and tables beeing more familiar (the same could be said of font-tags then, but i dont think many uses them any more), its a case of CSS not supporting 2D grid-layouts.
As painful as it is to admit, the amalgamation of rendering engines that’s currently available on the market simply does not provide a robust enough implementation of the 2-dimensional layout grid. You can float and slide your divs and spans all you want — these hacks will still remain hacks, because they only provide a simulation of the layout that’s aced by those darn tables.
We can rebuild him. We have the technology
So, what’s the solution? How do we keep provide a 2-dimensional grid to support the layout of our markup structurally correct markup? Well, how about this: let’s augment the rendering engine with the missing layout capabilities.
Huh? Are we trying to build a new browser now? Hopefully not. Just want to improve the existing ones with a slight twist of Javascript. Pull out the old trusty blade of client-side scripting to enforce the placement of your content areas in a 2-dimensional grid. Many of you ‘scripters probably just saw an elaborate mesh of “onresizes” and hidden iframes to account for various size changes flash before your eyes. No, it’s actually much simpler than that. Let’s think for a second. Don’t we already have a nice logic that does exactly what we need and is already build right into each and every one browser on this planet? Let’s see, what do we call those…? Tables, was it? You betcha.
The gory details
Here’s the gist of the technique: using standard DOM methods, inject a table where it is needed to support layout. Let’s use this composition as an example. Now, you fancy design folk might start pooh-poohing the awful choice of colors and lack of style, but let’s try to keep our focus on the topic.
First, we write the markup for the content and its structure:
<div id="Page"> <h1 class="Title">Your Company Name</h1> <ul class="Tools"> <li> <a href="#">Home</a> </li> <li> <a href="#">Site Map</a> </li> <li> <a href="#">Search</a> </li> <li> <a href="#">Contact Us</a> </li> </ul> <div class="Content"> <p> Lorem ipsum etc ... </p> </div> <ul class="Links"> <li> <a href="#">Related Link</a> </li> <li> <a href="#">Another Related Link</a> </li> <li> <a href="#">Vaguely Relevant Link</a> </li> <li> <a href="#">Just some link</a> </li> </ul> </div>
Next, we inject our tables:
var page = document.getElementById("Page"); var pageItems = new ClassNameBag(page); var pageTable = new LayoutTable(2, 2); pageTable.appendChild(1, 1, pageItems.Title); pageTable.appendChild(2, 1, pageItems.Tools); pageTable.appendChild(1, 2, pageItems.Content); pageTable.appendChild(2, 2, pageItems.Links); page.appendChild(pageTable.node);
And finally, we use CSS to complete the composition. Looks simple enough? Let’s examine the injection code line by line. Line 1 should be pretty self-explanatory: the element by id of “Page” is retrieved. Basically, we grab the root of the DOM tree that represents mentioned above markup.
Line 2 is just creating a ClassNameBag instance, which doesn’t relate to the technique directly, but is useful, because it creates an object with properties, each containing the child of the element, supplied as a constructor argument, and named after the child’s class name. Now, for instance, I can access <ul class="Links">
element simply as pageItems.Links.
On Line 3, the actual injection magic begins: a new object named LayoutTable is instantiated. The LayoutTable
encapsulates the “to-be-injected” table and its constructor has the following signature: LayoutTable(columnCount, rowCount [, className])
, where columnCount
and rowCount
specify how many columns and rows, correpondingly the table will contain, and the optional className
parameter allows to add an extra class name qualifier to table’s “class” attribute (which already has “Layout” class name assigned by default).
The LayoutTable
provides nprovides a single method appendChild(column, row, node)
, which is similar to your typical element.appendChild
method, except it appends the element, supplied in the node argument in the specified row
and column
of the table. Lines 4-7 distribute the contents of the Page element to their corresponding locations in the 2D grid (a.k.a. layout table).
And finally, on Line 8 we attach the layout table back to the Page element. The LayoutTable has one property node, which contains the actual DOM node of the table that was built with the code above. At this point, the injection is complete, and the table becomes part of the DOM.
In order to allow for more styling flexibility, each table row is given className of “ry“, where y is the number of the row (“r1” and “r2” in our example), and table each cell is assigned className of “cx“, where x is the number of the column (“c1” and “c2” for our table). This simple naming trick lets us control the presentation of rows, columns, or even individual cells in the layout table. For instance, “table.Layout tr.r1” will select the whole row 1, “table.Layout td.c2” will select the column 1, and “table.Layout tr.r1 td.c2” selects just the second cell of the first row.
Suddenly, CSS and tables are friends again. The old nemesis of structural markup comes back to the good guys’ side and saves the day. The barriers are broken, problems are solved, the happy end ensues, and tears of delight flow down the cheeks of sentimental readers.
What’s your name, stranger?
Now hang on just a second. Obviously, we have a technique here. And as far as I can tell, nobody had named it yet. Maybe I should try my hand at the Four-Letter Moniker Wheel of Fortune. Let’s see here… It’s a Table Injection Technique! Well, that’s only three letters and a couple of notches past PG-13. How about this: BOTOX, a quick way to smooth over nasty old wrinkles by injecting normally toxic stuff into the facial area of your page. Clever, but nah… I can just smell the lawyers circling around my head with this one.
Wait, wait.. I think I got it. The Table Injection for Layout Technique, or TILT! Woo wee! I think I just got me my own 4-letter abbreviation! Yup, TILT it is. Are you ready to TILT it?
Surgeon general’s warning
Just like anything that sounds too good to be true, TILT comes at a certain price.
- Using client-side scripting to add extra layout functionality to your browser’s rendering engine requires, uhm, client-side scripting, which may or may not be available on the browser of your visitor.
- TILT modifies the DOM tree of the page, which is something that has to be accounted for if you are doing DOM work for other purposes.
- Using tables for layout while keeping the markup clean is addictive and may lead to inexplicable waves of feeling joy and urge to hug and kiss co-workers and even total strangers.
Ok, maybe not the last one. However, when all said and done, this technique definitely deserves a good look. It has potential of making your hacking with CSS columns and floats obsolete and clearing up the way for you to actually starting to enjoy your markup.