Stylist Logo

Another Reason to Use Table-Based Layout

This entry is part 12 of 15 in the series Stylist

ABSTRACT

I often like to “frame” parts of a page. This can be to give effects such as embossing, cut-outs, drop-shadows, or even “picture frames” for images. In most cases, using block elements is the simplest and most effective method. However, the flexibility and robustness of table elements can be extremely useful

The code in this posting will be a bit more complex than in previous postings, and will assume a bit of prior knowledge of CSS.

THE BASICS

The following examples will make use of the following 12 images:

div_top.gif

div_middle.gif

div_bottom.gif

one_pixel.gif

top_left_corner.gif

top.gif

top_right_corner.gif

left.gif

right.gif

bottom_left_corner.gif

bottom.gif

bottom_right_corner.gif

These will comprise the “frame” for a section of a page.

We’ll start with the standard way that mortised backgrounds (including frames) are done with block-level elements:

Example 5

<html>
	<head>
		<title>Intermediate CSS, Example 5</title>
		<style type="text/css">
			body {background-color: black; text-align: center; color:#777}
			#container_div
				{
				width:300px;
				margin-left:auto;
				margin-right:auto;
				text-align:left
				}
			#top_row
				{
				height: 9px;
				background-image: url(images/div_top.gif);
				}
			#middle_row
				{
				background-image: url(images/div_middle.gif);
				background-repeat: repeat-y;
				padding-left: 12px;
				padding-right: 11px;
				}
			#bottom_row
				{
				height: 10px;
				background-image: url(images/div_bottom.gif);
				}
		</style>
	</head>
	<body">
		<div id="container_div">
			<div id="top_row"></div>
			<div id="middle_row">This is an example of a &quot;framed&quot; &lt;div&gt; element. It uses the most common technique for applying flexible backgrounds to block elements. Notice how much simpler the style and markup are. the drawback is that the width needs to be fixed if you use this technique, so see what happens at some of the very largest text sizes (which is why the text color is grey).</div>
			<div id="bottom_row"></div>
		</div>
	</body>
</html>

The way that this works is that we use the background-image CSS property to present the images used for the “frame.” The top and bottom are each presented using a single image, which is no problem. The block is a predictable and fixed size. Notice how simple the code is for this example. In 90% of sites, this is the best way to get this effect.

However, if you have a block that is not predictable, or that can have content overflowing the prescribed bounds, a <table> element is probably best:

Example 6

<html>
	<head>
		<title>Intermediate CSS, Example 6</title>
		<style type="text/css">
			body {background-color: black; text-align:center; color:#777}
			table {width:300px; margin-left:auto; margin-right:auto; text-align:left}
			#top_left_corner
				{
				height: 9px;
				width: 12px;
				background-image: url(images/top_left_corner.gif);
				}
			#top_side
				{
				height: 9px;
				background-image: url(images/top.gif);
				background-repeat: repeat-x;
				}
			#top_right_corner
				{
				height: 9px;
				width: 11px;
				background-image: url(images/top_right_corner.gif);
				}

			#left_side
				{
				width: 12px;
				background-image: url(images/left.gif);
				background-repeat: repeat-y;
				}
			#left_side img
				{
				width: 12px;
				}
			#content_area
				{
				background-color:#ccf;
				}
			#right_side
				{
				width: 11px;
				background-image: url(images/right.gif);
				background-repeat: repeat-y;
				}
			#right_side img
				{
				width: 11px;
				}
			#bottom_left_corner
				{
				height: 10px;
				width: 12px;
				background-image: url(images/bottom_left_corner.gif);
				}
			#bottom_side
				{
				height: 10px;
				background-image: url(images/bottom.gif);
				background-repeat: repeat-x;
				}
			#bottom_right_corner
				{
				height: 10px;
				width: 11px;
				background-image: url(images/bottom_right_corner.gif);
				}
		</style>
	</head>
	<body">
		<table cellpadding="0" cellspacing="0">
			<tr>
				<td id="top_left_corner"></td>
				<td id="top_side"></td>
				<td id="top_right_corner"></td>
			</tr>
			<tr>
				<td id="left_side"><img src="images/one_pixel.gif" /></td>
				<td id="content_area">This is an example of a &quot;framed&quot; table. The center &lt;td&gt; element contains the content, and the surrounding &lt;td&gt; elements comprise the &quot;frame.&quot; This is an extremely flexible arrangement, as the table can be stretched and resized. It will always present a proper frame, and will accommodate many varieties of content. For example, try resizing the font size in your browser. No matter how large you make it, the frame will display consistently.</td>
				<td id="right_side"><img src="images/one_pixel.gif" /></td>
			</tr>
			<tr>
				<td id="bottom_left_corner"></td>
				<td id="bottom_side"></td>
				<td id="bottom_right_corner"></td>
			</tr>
		</table>
	</body>
</html>

In this case, the frame is totally flexible, and will maintain its shape and borders, regardless of the context or the content. However, you give up the power to reposition or radically alter the page. You will notice that the code is also a great deal more complex.

The two one-pixel transparent GIF images in the side <td> elements are because some browsers don’t render empty table cells properly under duress (when the table has been stretched by its content beyond the specified size). Inserting these images ensures that the table columns will be displayed correctly.

#left_side img
	{
	width: 12px;
	}
	
#right_side img
	{
	width: 11px;
	}
	
<td id="left_side"><img src="images/one_pixel.gif" /></td>

<td id="right_side"><img src="images/one_pixel.gif" /></td>

So, why do we need tables? Can’t we do this with block elements? I thought that tables were throwbacks to an archaic way of design, and that I would be banned from the “Cool Kids Club” forever if I insist on using them?

Well, that’s a good question, Timmy. Let’s use the Power of the Div™ to do the same thing:

Example 7

<html>
	<head>
		<title>Intermediate CSS, Example 7</title>
		<style type="text/css">
			body
				{
				background-color: black;
				text-align:center;
				color: #777;
				}
			#container_div
				{
				width: 300px;
				margin-left: auto;
				margin-right: auto;
				}
			#top_left_corner
				{
				float: left;
				height: 9px;
				width: 12px;
				background-image: url(images/top_left_corner.gif);
				}
			#top_side
				{
				float: left;
				height: 9px;
				width: 277px;
				background-image: url(images/top.gif);
				background-position: left top;
				background-repeat: repeat-x;
				}
			#top_right_corner
				{
				float: left;
				height: 9px;
				width: 11px;
				background-image: url(images/top_right_corner.gif);
				}

			#left_side
				{
				clear: left;
				float: left;
				width: 12px;
				background-image: url(images/left.gif);
				background-position: left top;
				background-repeat: repeat-y;
				}
			#middle_content
				{
				float: left;
				width: 277px;
				background-color:#ccf;
				text-align: left;
				}
			#right_side
				{
				float: left;
				width: 11px;
				background-image: url(images/right.gif);
				background-position: right top;
				background-repeat: repeat-y;
				}

			#bottom_left_corner
				{
				clear: left;
				float: left;
				height: 10px;
				width: 12px;
				background-image: url(images/bottom_left_corner.gif);
				}
			#bottom_side
				{
				float: left;
				height: 10px;
				width: 277px;
				background-image: url(images/bottom.gif);
				background-position: left bottom;
				background-repeat: repeat-x;
				}
			#bottom_right_corner
				{
				float: left;
				height: 10px;
				width: 11px;
				background-image: url(images/bottom_right_corner.gif);
				}
		</style>
	</head>
	<body">
		<div id="container_div">
			<div id="top_left_corner">&nbsp;</div>
			<div id="top_side">&nbsp;</div>
			<div id="top_right_corner">&nbsp;</div>
			<div id="left_side">&nbsp;</div>
			<div id="middle_content">This is an example of a &quot;framed&quot; &lt;div&gt; element. This uses a technique smilar to that used by the &lt;table&gt; element frame. However, as you can see, it doesn't work. Notice that the sides don't go all the way down. This is because block elements don't display in the same manner as tables. We are using the &quot;float&quot; CSS property to get the &lt;div&gt; elements to mortise together like table cells do. However, the float property basically decouples the block element from the vertical context, and places it at a slightly higher z-index. If this worked, then this method would give the same flexibility as using tables.</div>
			<div id="right_side">&nbsp;</div>
			<div id="bottom_left_corner">&nbsp;</div>
			<div id="bottom_side">&nbsp;</div>
			<div id="bottom_right_corner">&nbsp;</div>
		</div>
	</body>
</html>

The problem here is that the #left_side and #right_side <div> elements are completely “decoupled” from the #middle_content <div>. They have no relation to each other, so they don’t know how far down the element goes. Because they only have a non-breaking space in them (&nbsp character -unnecessary in actual practice. I just have them here to give a bit of “body” to the empty elements), then they only occupy a small vertical space. Remember that a <div> element that doesn’t have a specific height assigned will adjust to its content (although the same cannot be said for the width). I’ve tried all kinds of things to make it work the way a table does. No dice.

You can get something approaching the way a table works by eliminating the #left_side and #right_side elements, and using the div_middle.gif image as a background for the #middle_content element:

Example 8

<html>
	<head>
		<title>Intermediate CSS, Example 8</title>
		<style type="text/css">
			body
				{
				background-color: black;
				text-align:center;
				color: #777;
				}
			#container_div
				{
				width: 300px;
				margin-left: auto;
				margin-right: auto;
				}
			#top_left_corner
				{
				float: left;
				height: 9px;
				width: 12px;
				background-image: url(images/top_left_corner.gif);
				}
			#top_side
				{
				float: left;
				height: 9px;
				width: 277px;
				background-image: url(images/top.gif);
				background-position: left top;
				background-repeat: repeat-x;
				}
			#top_right_corner
				{
				float: left;
				height: 9px;
				width: 11px;
				background-image: url(images/top_right_corner.gif);
				}

			#middle_content
				{
				clear: left;
				background-image: url(images/div_middle.gif);
				background-repeat: repeat-y;
				padding-left: 12px;
				padding-right: 11px;
				text-align: left;
				}

			#bottom_left_corner
				{
				clear: left;
				float: left;
				height: 10px;
				width: 12px;
				background-image: url(images/bottom_left_corner.gif);
				}
			#bottom_side
				{
				float: left;
				height: 10px;
				width: 277px;
				background-image: url(images/bottom.gif);
				background-position: left bottom;
				background-repeat: repeat-x;
				}
			#bottom_right_corner
				{
				float: left;
				height: 10px;
				width: 11px;
				background-image: url(images/bottom_right_corner.gif);
				}
			#content_breaker
				{
				clear: both;
				}
		</style>
	</head>
	<body">
		<div id="container_div">
			<div id="top_left_corner"></div>
			<div id="top_side"></div>
			<div id="top_right_corner"></div>
			<div id="middle_content">This is an example of a &quot;framed&quot; &lt;div&gt; element. This uses a technique smilar to that used by the &lt;table&gt; element frame. In this case, we use a solid wide image as the background for the content element, so it does go all the way down. However, this element cannot be resized the way you can a table.</div>
			<div id="bottom_left_corner"></div>
			<div id="bottom_side"></div>
			<div id="bottom_right_corner"></div>
		</div>
	</body>
</html>

This is more or less a “hybrid” between Example 5 and Example 7. It really isn’t worth it, as it suffers from the same issue that affected Example 5: It is completely fixed and inflexible. It is also a great deal more complex in both markup and style. A table-based layout could be stretched and crammed with the most obnoxious content, and will retain its proper shape and behavior.

CONCLUSION

So, don’t use <table> elements, except when you have to. Some of the kids might yell at you and call you names, but just do what you need to do anyway. This will work, and it will work very, very well. There are no “single, hard and fast rules” that apply to all cases. Learn to be flexible. It will make your life, and the lives of your site visitors, a great deal more pleasant.

Don’t Tell Them I Told You This

The “dirty little secret” about table elements, is that they are really just pre-loaded block elements, with different inherent styles, and a prescribed hierarchy. You can radically change the way that tables work. For example, the “common wisdom” is that you can’t make tables responsive.

As it turns out, you can. Quite easily. Just use CSS to change the display type to “block” for the selected viewport size, and the table magically becomes a div. I’ve done it more than once, in an effort to make old-fashioned table-layout sites responsive without rewriting everything.

I also use the “display:table” rule a fair bit to make divs useful. Look at the table of contents at the top of this page for an example.

Next, we’ll go over positioned block elements. This is where CSS-based design can get fun.