Developer

Moving toward table-free forms

Web designers struggle to present form controls and text labels in a readable and attractive manner. The typical solution has been HTML tables, but thanks to CSS, you can build forms that avoid tables and separate content from presentation.


This article originally appeared in the Design & Usability Tactics newsletter. Click here to register automatically for the newsletter.

HTML forms have presented Web builders with a design challenge since the earliest days of the Web. The problem has been how to arrange the various form controls (i.e., text boxes, radio buttons, and check boxes) and the text labels that go with them in a readable and attractive manner. The trend toward separating content from presentation is causing Web builders to rethink how they respond to this challenge.

For years, the standard solution to positioning form elements has been to use tables to align and position the form controls and text labels. Many Web builders continue the practice despite the fact that tables are no longer the recommended way to control page layout and positioning for anything but pure tabular matter. Many sites that have switched to the recommended structural HTML/XHTML markup and separate CSS presentation styles for most pages still fall back on table-based layout for forms.

The reluctance to abandon tables for form layout is undoubtedly because the obvious alternative to tables—using <div>tags in place of the <td> tags that define table cells—doesn't provide a compelling advantage over tables. Using <div> instead of table cells does get around the admonition against using tables for page layout, but surrounding each form element with a <div> produces page markup that is only slightly less cluttered than the markup for a table.

One way to avoid cluttering your page markup with a lot of <div> tags is to use CSS to style another tag that is (or should be) already present in the form—the label tag. This tag binds a text label to a form control either by wrapping around both the text and the form element or by using the for attribute of the label tag to link to the id attribute of the form element.

The test case
As a test of the various form layout options, I used three different techniques to reproduce the same simple form. The form consists of three text input boxes (for name, address, and phone number) and their labels, stacked on top of each other, with a submit button at the bottom of the form.

Simply entering each text label followed by the input box and a <br> (line break) produces stacked text boxes that are staggered unattractively. The goal is to align the text input boxes in a vertical column and position each text label flush right against the left side of the associated box. The submit button goes in the bottom row, aligned with the left edge of the text boxes.

Form formatting: The old way
The standard way to align the form elements is with a table, which, in this case, is a table consisting of two columns and four rows. The left column holds the text labels and gets a fixed width and right alignment; the right column aligns the text input boxes and submit button.
<form id="form1" method="post" action="mailform.cgi">
  <table width="100%" border="0" cellspacing="0" cellpadding="0">
    <tr>
      <td width="150" align="right">
        <label for="username">Name:&nbsp;</label>
      </td>
      <td>
        <input type="text" id="username" />
      </td>
    </tr>
    <tr>
      <td align="right">
        <label for="addrs">Address:&nbsp;</label>
      </td>
      <td>
        <input type="text" id="addrs" />
      </td>
    </tr>
    <tr>
       <td align="right">
        <label for="phn">Phone:&nbsp;</label>
      </td>
      <td>
        <input type="text" id="phn" />
      </td>
    </tr>
    <tr>
      <td align="right">
        &nbsp;
      </td>
      <td>
        <input type="submit" name="Submit" value="Submit" />
      </td>
    </tr>
  </table>
</form>

The form content is almost lost in the nested table, row, and cell tags that define the table structure. Also, the table tags include presentation attributes for size and alignment, and some of those attributes must be repeated for each cell. Note that each text label is followed by a character nonbreaking space (&nbsp;) to create a space between the colon in the label and the text input box.

Form formatting with <div>
Replacing the table with <div> helps clean things up some. There's a <div> for each table cell above, but there's no need to nest table cells within a table row within a table, so fewer <div> tags are required to get the job done. Also, we can pull the presentation attributes that control the size and alignment of the left column out into a CSS style rule and apply it by assigning the appropriate class to the <div> tags that make up that column.

Here's the CSS code for the <div> tags in the left column:
 formlabel {
    clear: left;
    float: left;
    width: 150px;
    padding-right: 2px;
    text-align: right;
}

Here's the markup:
<form id="form1" method="post" action="mailform.cgi">
  <<div> class="formlabel">
    <label for="username">Name: </label>
  </<div>>
  <div>
    <input type="text" id="username" />
  </<div>>
  <<div> class="formlabel">
    <label for="addrs">Address: </label>
  </<div>>
  <<div>>
    <input type="text" id="addrs" />
  </<div>>
  <<div> class="formlabel">
    <label for="phn">Phone: </label>
  </<div>>
  <<div>>
    <input type="text" id="phn" />
  </<div>>
  <<div> class="formlabel">
    &nbsp;
  </<div>>
  <<div>>
    <input type="submit" name="Submit" value="Submit" />
  </<div>>
</form>

The clear and float attributes cause each left-column <div> to align with the left side of the form and allow the following <div> (which contains the text input box) to flow into position immediately to the right of its label. The width and text-align attributes set the size of the column and text alignment. The padding-right attribute creates a small space between the label and the text input box, which eliminates the need for the nonbreaking space characters in the labels.

Form formatting: Another approach
We can simplify the form markup even further by eliminating most of the <div> tags. There's no need for <div> tags around the text labels in the left column because the label tag that already surrounds the text will accept the same CSS style class as the <div> tags above. The <div> tags could remain around the input tags in the right column, but a single <br> at the end of each row will also get the job done. A lone <div> remains in the left column of the bottom row to ensure that the submit button ends up in the right column, under the text input boxes.

And here's the HTML markup:
<form id="form1" method="post" action="mailform.cgi">
  <label for="username" class="formlabel">Name: </label>
  <input type="text" id="username" /><<br> />
  <label for="addrs" class="formlabel">Address: </label>
  <input type="text" id="addrs" /><<br> />
  <label for="phn" class="formlabel">Phone: </label>
  <input type="text" id="phn" /><<br> />
  <<div> class="formlabel">&nbsp;</<div>>
  <input type="submit" name="Submit" value="Submit" />
</form>

Using tables to align form elements is a hard habit for many Web builders to break. In some ways, it's even harder to give up tables for form layout than it is for general page layout. However, with a little thought, you can find ways to produce comparable results with structural markup and CSS. And when you do, you'll be rewarded with code that is faster, more concise, and easier to read and maintain.

Editor's Picks