Chapter 2
CSS Table Layout

When released, Internet Explorer 8 will support many new values for the CSS display property, including the table-related values: table, table-row, and table-cell—and it’s the last major browser to come on board with this support. This event will mark the end of complex CSS layout techniques, and will be the final nail in the coffin of using HTML tables for layout. Finally, producing table-like grid layouts using CSS will be quick and easy.

Applying table-related display property values to web page elements causes the elements to mimic the display characteristics of their HTML table equivalents. In this chapter, I’ll demonstrate how this will have a huge impact on the way we use CSS for web page layouts. However, before we can understand why CSS tables will be so useful to us, we need to have a quick look at current popular CSS layout methods and the problems we have with them.

Using Current Layout Techniques

As mentioned in Chapter 1, in order to coerce what effectively consists of a vertical stack of block elements into a grid-like layout, CSS authors have used absolute positioning or floating to force blocks to sit alongside each other. These techniques, while thoroughly tested in all modern browsers, are complex and not without problems. So let’s have a quick review of how to build a simple three-column layout—such as the one shown in Figure 2.1—using current techniques.

The layout we want to create

Figure 2.1. The layout we want to create

Here’s an excerpt of the XHTML markup we’ll be using to create this web page for the Tidmouth Zoo and Animal Park, showing the major structural elements:

3col-absolute.html (excerpt)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
  <head>
    … HTML head content…
  </head>
  <body>
    <div id="wrapper">
      <div id="header">
        … top graphic banner…
      </div>
      <div id="main">
        <div id="content">
          … main article content…
        </div>
        <div id="nav">
          … navigation column content…
        </div>
        <div id="extras">
          … news headlines column content…
        </div>
      </div>
      <div id="footer">
        … page footer content…
      </div>
    </div>
  </body>
</html>

You’ll be able to view the complete code and all supporting files for the page by downloading the code archive for this book from the SitePoint web site.

Absolute Positioning

The first technique is to use absolute positioning. This is one of the more common layout techniques used today, and probably the easiest to implement. This layout works by making space for the two narrower columns, giving the main content area a large left margin, then positioning the two columns on top of the margin. We’ll focus on the CSS required for the layout of the major structural elements, but the complete CSS is available in the code archive.

We begin by setting a width for the wrapper div element, setting a height and background width for the header div, and defining the appearance of the footer div:

3col-absolute.css (excerpt)
#wrapper {
  position: relative;
  text-align: left;
  width: 760px;
  margin-right: auto;
  margin-left: auto;
}
#header {
  height: 180px;
  background-image: url(images/header.jpg);
  background-repeat: no-repeat;
  padding-bottom: 10px;
}
#footer {
  border-top: 2px solid #d7ad7b;
  background-color: #e7dbcd;
  font-size: 80%;
  padding: 0.2em 10px 0.2em 0;
  text-align: right;
}

The main div is the parent element for our main content area and the two narrow columns. First, we’ll relatively position the main div and then give the content div a left margin wide enough to fit the two other columns:

3col-absolute.css (excerpt)
#main {
  position: relative;
}
#content {
  margin-left: 380px;
}

If we check out the layout in a browser, we’ll see Figure 2.2—the content column is sitting nicely over to the right-hand side, leaving a 380px left margin, which is where we’ll place the other two columns.

Content column with space left for other columns

Figure 2.2. Content column with space left for other columns

The final step is to absolutely position the two remaining columns within the space provided by the content div’s left margin:

3col-absolute.css (excerpt)
#nav {
  position: absolute;
  top: 0;
  left: 0;
  width: 180px;
  background-color: #e7dbcd;
}
#extras {
  position: absolute;
  top: 0;
  left: 190px;
  width: 180px;
}

An absolutely positioned element is positioned relative to its closest positioned ancestor. In our case, the main div has been relatively positioned, so our two columns are positioned relative to its position in the page layout. Figure 2.3 shows how the absolutely positioned three-column layout appears in Firefox.

Problems with This Technique

As you can see in Figure 2.3, the background color of the nav column only extends as far as the content allows, whereas our original design calls for a full-length column.

We can circumvent this issue by adding a background image to the container element of the columns. This technique is described as faux columns, popularized in a well-known Alistapart article by Dan Cederholm.

The absolutely positioned layout in Firefox Firefox absolutely positioned layout in

Figure 2.3. The absolutely positioned layout in Firefox

In the case of our web page, the main div wraps all three columns, so we can add a background image to that element and position the image to appear behind the nav column—giving the appearance of a full-height column. The image also includes the dotted border that appears along the right-hand edge of the extras column. We achieve this effect in CSS by positioning the image shown in Figure 2.4 as a background image that repeats vertically.

The background image

Figure 2.4. The background image

All we need to do is add a couple of extra declarations to our style sheet:

3col-absolute.css (excerpt)
#main {
  position: relative;
  background-image: url(images/main-bg.gif);
  background-repeat: repeat-y;
}

The result is the full-height column shown in Figure 2.5.

The finished absolutely positioned layout in Firefox

Figure 2.5. The finished absolutely positioned layout in Firefox

While all seems fine with the current layout, another problem arises if the content in the content column ends up shorter than the content of the nav column. As demonstrated in Figure 2.6, the footer will end up displaying across the content of all the columns instead of at the bottom of the page.

The reason for this problem is that absolutely positioning an element will remove it from the document flow. In our layout above, we’ve removed the two left-hand columns from the document flow and placed them on top of the other elements. The content and footer elements remain within the document flow, and are positioned by the browser under the content div.

The footer problem

Figure 2.6. The footer problem

To combat this problem, you can add a top margin to the footer element, or bottom padding to the content element, to ensure the footer is an appropriate distance from the content. Neither of these options are particularly desirable; ideally, we’d like our footer to sit neatly under our content. However, beggars can’t be choosers; if you can’t guarantee the amount of content in the main content column, you may need to consider these approaches.

Finally, if you view this layout in Internet Explorer 6, you’ll notice a big problem. The nav and extras columns aren’t positioned correctly, as you can see in Figure 2.7.

Layout problem in IE6

Figure 2.7. Layout problem in IE6

If we add the following width declaration to our style sheet, IE6 will be able to display our layout correctly:

3col-absolute.css (excerpt)
#main {
  position: relative;
  width: 100%;
  background-image: url(images/main-bg.gif);
  background-repeat: repeat-y;
}

The above width declaration is in fact redundant, as we’ve also specified widths for all the columns; however, in this situation, it’s a safe way to correct the layout in IE6 without affecting the layout in any other browser.

Note: A Note on Internet Explorer and hasLayout

In Internet Explorer 6 and 7, applying a width declaration to an element causes the element to “gain a layout.” It’s far beyond the scope of this book to explain what gaining a layout means and why it’s important in Internet Explorer—to put it in a nutshell, it means the main div will look after the layout of its child elements if it has a layout.

When an element has a layout, IE6 and 7’s magical hasLayout property is set to true. There are other ways to trigger this property, including valid actions such as setting a height or floating the element, as well as those that involve adding IE-specific properties that don’t validate, such as setting the zoom property to 1. If the concept of hasLayout is new to you, you might like to have a look at The SitePoint CSS Reference—you’ll find IE6 and 7 bugs far easier to deal with, once you understand the concept.

Floated Layout

The alternative to absolute positioning is a floated layout; by using the CSS float property, we can cause the column div elements to float alongside each other. We only need to make these few small changes to our CSS to turn our absolutely positioned layout into a floated layout:

3col-float.css (excerpt)
#nav {
  float: left;
  margin-right: 10px;
  width: 180px;
  background-color: #e7dbcd;
}
#extras {
  float: left;
  width: 180px;
}
#content {
  float: right;
  width: 380px;
}

Using the CSS float property, we’ve specified that the nav and extras columns will be floated to the left-hand side, while the content column will be floated to the right-hand side.

Problems with This Technique

If we test the layout after making those changes, we discover the disaster pictured in Figure 2.8.

Floated layout disaster

Figure 2.8. Floated layout disaster

The biggest problem with floated layouts is the need to clear the floated elements, to stop elements following a floated element from wrapping around the floated element. As our columns are now floated—removing them from the document flow—the main div, having no other content, has no height or width. Consequently, the background image is no longer visible and the content of the footer is wrapping around the contents of the columns as it is supposed to do, being a non-floated element. There’s more detailed information about floating and clearing in the SitePoint CSS Reference.

There are various methods of clearing, but one of the most simple, reliable, and commonly used methods is to put a redundant div element under the columns that need to be cleared. First, we add an empty div element with a class value of clear just before the closing tag of the main div:

3col-float.html (excerpt)
<div id="main">
  …
  <div class="clear">&nbsp;</div>
</div>

Next we add a CSS rule for the clear class:

3col-float.css (excerpt)
.clear {
  clear: both;
}

The value both clears all the columns, whether floated left or right. Simply clearing the footer would solve the footer problem but still leave our background missing. This method enables us to add a footer and have it sit below whichever of the three columns is the longest, as shown in Figure 2.9.

Adding bits of redundant markup isn’t too much of a problem when you simply need to clear the three main columns of a layout. However, when using this technique on lots of small areas of a layout, this extra markup does add up to create a page that’s larger, more complicated, and harder to maintain than what’s ideal.

Floated layout disaster averted

Figure 2.9. Floated layout disaster averted

For those interested in other options for clearing CSS floats without extra markup, a useful rundown of these techniques can be found on Robert Nyman’s blog. However, these other techniques can be complicated and have their limitations; this pragmatist has found that often the most robust method is the one outlined above—even if it does add a bit of “weight” to the page.

All this talk about clearing floats has also obscured the fact that the background image is still required to achieve a full-height column background, just as it was with the absolutely positioned layout. Also, we still require our width declaration for the main div element to make the layout work in IE6.

As we’ve seen, the current methods of laying out pages do have their problems. Developers can negotiate many of the problems by combining the two outlined methods in one layout—perhaps by using a floated main layout, but employing positioning for internal elements. However, just to achieve the relatively simple and common layout of three columns with a footer does require a good understanding of CSS positioning and the issues involved.

It’s no wonder that there’s a very vocal contingent within the web design community who have maintained throughout the rise of CSS-based layout that sticking with HTML tables is the easiest way to lay out a web page—just for the luxury of being able to set a background image on a column and have it extend all of the way down the page!

What is needed is a CSS technique that provides the simplicity of grid layouts with HTML tables, without having to use table markup.

Using CSS Tables

CSS tables solve all the layout issues we’ve explored here regarding positioning and backgrounds in modern browsers. Specifying the value table for the display property of an element allows you to display the element and its descendants as though they’re table elements. The main benefit of CSS table-based layouts is the ability to easily define the boundaries of a cell so that we can add backgrounds and so on to it—without the semantic problems of marking up non-tabular content as a HTML table in the document.

Before we dive in and discover how this works, let’s create an instant demonstration. First, we make a few small changes to our markup:

3col-csstable.html (excerpt)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US">
  <head>
    … HTML head content…
  </head>
  <body>
    <div id="wrapper">
      <div id="header"></div>
      <div id="main">
        <div id="nav">
          … navigation column content…
        </div>
        <div id="extras">
          … news headlines column content…
        </div>
        <div id="content">
          … main article content…
        </div>
      </div>
    </div>
  </body>
</html>

We’ve rearranged the HTML source so that the source order matches the content display order. The nav column comes first, followed by the extras column, and then the content column.

We also need to apply the following CSS modifications:

3col-csstable.css (excerpt)
#main {
  display: table;
  border-collapse: collapse;
}
#nav {
  display: table-cell;
  width: 180px;
  background-color: #e7dbcd;
}
#extras {
  display: table-cell;
  width: 180px;
  padding-left: 10px;
  border-right: 1px dotted #d7ad7b;
}
#content {
  display: table-cell;
  width: 380px;
  padding-left: 10px;
}

The fresh CSS table-based layout that we’ve just created will display correctly in Internet Explorer 8 as well as in Firefox, Safari, and Opera; Figure 2.10 shows how it looks in IE8.

The CSS table-based layout in Internet Explorer 8

Figure 2.10. The CSS table-based layout in Internet Explorer 8

Our three-column equal-height layout is achieved without having to resort to tricks like faux columns using background images, worrying about positioning, or having to clear floats—revolutionary!

How Does This Work?

The display property allows you to specify a range of table-related values in order to make elements display as though they were table elements. The available display values are:

table

makes the element behave like a table element

table-row

makes the element behave like a table row (tr) element

table-cell

makes the element behave like a table cell (td) element

table-row-group

makes the element behave like a table body row group (tbody) element

table-header-group

makes the element behave like a table header row group (thead) element

table-footer-group

makes the element behave like a table footer row group (tfoot) element

table-caption

makes the element behave like a table caption element

table-column

makes the element behave like a table column (col) element

table-column-group

makes the element behave like a table column group (colgroup) element

Note: Hang on … Aren’t Tables for Layout Wrong?

Perhaps you’re feeling slightly uncomfortable about the example we’ve just seen—after all, haven’t web standards advocates like myself been insisting for years that you shouldn’t be using tables for layout?

The table element in HTML is a semantic structure: it describes what data is. Therefore, you should only use the table element if the data you are marking up is tabular—for example, a table of financial information. If it would normally be stored in a spreadsheet on your computer, it probably needs marking up as a table in HTML.

The table value of the display property, on the other hand, is simply an indication of how something should look in the browser—it has no semantic meaning. Using a table element for your layout tells a user-agent, “This data is tabular.” Using a bunch of divs that have the display property set to table and table-cell says nothing to that user-agent other than asking it to render them visually in a certain way, if it’s capable of doing so.

Of course, we should also take care not to use display: table; on a bunch of div elements when what we really have is tabular data!

Our simple example above makes our layout behave as if it were a single row table with three cells; it doesn’t take much imagination to realize the potential of this technique for creating complex grid layouts with ease.

Anonymous Table Elements

CSS tables happily abide by the normal rules of table layout, which enables an extremely powerful feature of CSS table layouts: missing table elements are created anonymously by the browser. The CSS2.1 specification states:

Document languages other than HTML may not contain all the elements in the CSS 2.1 table model. In these cases, the “missing” elements must be assumed in order for the table model to work. Any table element will automatically generate necessary anonymous table objects around itself, consisting of at least three nested objects corresponding to a “table”/“inline-table” element, a “table-row” element, and a “table-cell” element.

What this means is that if we use display: table-cell; without first containing the cell in a block set to display: table-row;, the row will be implied—the browser will act as though the declared row is actually there.

Let’s use a simple example to investigate this feature: the three-cell grid layout shown in Figure 2.11. We’ll look at three different HTML markup samples that will result in the same visual layout.

A simple grid layout

Figure 2.11. A simple grid layout

First, here’s a sample of markup that can be used to generate the three-cell layout:

<div class="container">
  <div class="row">
    <div class="cell">CELL A</div>
    <div class="cell">CELL B</div>
    <div class="cell">CELL C</div>
  </div>
</div>

A set of nested div elements may not seem so very exciting, but hang in there, we’re building to something. The CSS is also very simple:

.container {
  display: table;
}
.row {
  display: table-row;
}
.cell {
  display: table-cell;
  width: 100px;
  height: 100px;
  border: 1px solid blue;
  padding: 1em;
}

The CSS above sets the element with a class of container to display: table, an element with a class of row to display: table-row, and an element with a class of cell to display: table-cell, as well as giving it a border and a height and width.

This HTML markup above explicitly creates elements for the table and row surrounding the three cells, using all of the CSS classes that we’ve created. However, we can reduce the markup, removing the row div element like so:

<div class="container">
  <div class="cell">CELL A</div>
  <div class="cell">CELL B</div>
  <div class="cell">CELL C</div>
</div>

Even though the above markup is missing the element representing the table row, the row will be created by the browser as an anonymous box. We can reduce the markup even further:

<div class="cell">CELL A</div>
<div class="cell">CELL B</div>
<div class="cell">CELL C</div>

The above markup is missing the elements representing the table row and the table; these are both created as anonymous boxes by the browser. Even with the elements missing in markup, the end product, shown in Figure 2.11, is the same.

Rules for the Creation of Anonymous Table Elements

These anonymous boxes are not created by magic, and they won’t automatically make up for any deficiencies in your HTML code. To be able to take full advantage of anonymous table elements, you’d best become familiar with the rules for their creation. If a layout calls for an implied element, the browser will create an anonymous box and set its CSS display property to one of table, table-row, or table-cell, depending on the context.

If you have an element that has been set to display: table-cell; but its immediate parent (the containing element) is not set to table-row, an anonymous box set to table-row will be created to enclose the cell and any subsequent adjacent sibling elements that are also set to table-cell, until it encounters an element not set to table-cell, so they’ll all end up in the same row. This is the case with the following markup:

<div class="cell">CELL A</div>
<div class="cell">CELL B</div>
<div class="cell">CELL C</div>
<div>Not a cell</div>

The three div elements above that have a class of cell are set to display: table-cell; and will appear side by side as though they’re in a single row table; the last div element won’t be included in the row, because it isn’t set to display: table-cell;.

If an element is set to display: table-row; while its parent element isn’t set to table (or table-row-group), an anonymous box set to display: table; will be created to enclose the row, and any subsequent adjacent sibling elements will be set to display: table-row. Also, if the element with display set to table-row lacks an element set to table-cell directly within it, an anonymous box set to table-cell will be created to enclose all the elements within the table-row element. Consider the following markup:

<div class="row">ROW A</div>
<div class="row">ROW B</div>
<div>Not a row</div>

The two div elements above with a class of row are set to display: table-row; and will appear one under the other as though they’re rows in the same single-column table. The last div element won’t be included in the implied table.

Similarly, if an element is set to any of the other display values that match elements which would naturally exist directly inside a parent table element such as table-row-group, table-header-group, table-footer-group, table-column, table-column-group, and table-caption, but does not have a parent set to display: table;, an anonymous box set to table will be created to enclose the element and any subsequent adjacent sibling elements with suitable display values.

Other Useful Table Properties

When using CSS tables, because the elements conform to the normal rules for table layout, you can also apply other table-related CSS properties. Here’s a few that can come in handy:

table-layout

Setting the table-layout to fixed tells the browser that the table should render with the fixed algorithm for formatting the cell widths. This is useful in a fixed-width layout, such as the one we created earlier.[2]

border-collapse

Just as with regular HTML tables, you can use the border-collapse property to specify that your table layout elements use collapsed (with the value collapse) or separated (with the value separate) borders between the cell elements.

border-spacing

If you specify the value separate for the border-collapse property, you can then use the border-spacing property to specify the width of the space between the cell element borders.

Making a Perfect Grid

Making a grid of equal height elements has always been a challenge using traditional CSS layout techniques, but it’s something to which CSS tables are well suited. For example, if we want to create an image gallery comprising a grid of images with captions, such as the one shown in Figure 2.12, using a CSS table renders the task simple.

The gallery grid demo in Internet Explorer 8

Figure 2.12. The gallery grid demo in Internet Explorer 8

The markup for our gallery is as follows:

csstable-grid.html (excerpt)
<div class="grid">
  <div class="row">
    <div class="image">
      <img src="images/photo1.jpg" alt="A Lily" />
      <p>A lily in the gardens of The Vyne Country House</p>
    </div>
    <div class="image">
      <img src="images/photo3.jpg" alt="A Fuchsia plant" />
      <p>Fuchsia plant in my garden</p>
    </div>
  </div>
  <div class="row">
    <div class="image">
      <img src="images/photo2.jpg" 
          alt="A crazy looking Allium flower" />
      <p>A crazy looking flower</p>
    </div>
    <div class="image">
      <img src="images/photo4.jpg"
         alt="A Robin sitting on a fence" />
      <p>This robin has been visiting our garden over the summer.
          He is very friendly and doesn't seem to be too worried
          about sharing the garden with us.</p>
    </div>
  </div>
</div>

Each gallery image cell is comprised of an img element and a caption in a p element contained within a div element with a class of image. Each row is contained within a div element within a class of row, and the whole gallery is contained within a div with a class of grid.

The CSS required to lay out our grid is simple:

csstable-grid.css (excerpt)
.grid {
  display: table;
  border-spacing: 4px;
}
.row {
  display: table-row;
}
.image {
  display: table-cell;
  width: 240px;
  background-color: #000;
  border: 8px solid #000;
  vertical-align: top;
  text-align: center;
}
.image p {
  color: #fff;
  font-size: 85%;
  text-align: left;
  padding-top: 8px;
}

The above CSS is fairly straightforward, but you might notice how we’ve made use of the border-spacing property to control the spacing of our gallery image cells. Making a grid layout couldn’t be easier—and we’ve avoided any headaches over equal heights or fragile layouts made with floated elements.

Putting Principles into Practice

This chapter has presented a basic primer to the usage of the table-related values of the CSS display property—finally, a source of relief for all those struggling to construct reliable grid-based layouts using CSS! We began by examining the current layout options of absolute positioning and floated elements, and the many issues to consider when implementing them. We then had an introduction to the straightforward approach to layout provided by CSS tables. We explored the various table-related display values available, looked at the nature of anonymous table elements, and discovered some other useful CSS table properties.

The next step is up to you—with any luck, you have realized the potential CSS tables provide for creating grid layouts, and are now bursting with curiosity! Using the knowledge gained in this chapter, you’re all set up to begin experimenting with your own CSS table layouts and create new techniques.

Now, we’ll move on to consider some of the most common questions about CSS table layouts in the next chapter, and provide concrete solutions.



[2] You can read more about how the table layout algorithms work in the SitePoint CSS reference, available at http://reference.sitepoint.com/css/tableformatting.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.77.36