Creating multicolumn lists

Until browsers support CSS 3 specifications, Web builders will need to rely on other techniques for creating multicolumn lists. Here's a look at some of the techniques that work with today's browsers.

The standard formatting for an HTML list is for the list items to be stacked, flush left, indented from the left margin, and preceded by a bullet or number. That works fine for a typical list, in which each item consists of a sentence or two of text. However, when the list items are very short (e.g., a long list of items composed of a word or two each), you end up with a tall, narrow column of text on the left side of your Web page with a big empty space to the right. That's when many Web designers start looking for ways to break a long list into multiple side-by-side columns instead of a single long column.

Eventually, CSS 3 will provide the tools to define snaking text columns, but it will be years before browsers will broadly support the CSS 3 specifications. Until then, Web builders will need to rely on other techniques for creating multicolumn lists. So, let's look at some of the techniques that work with today's browsers.

Technique 1: Floating list items

One of the simpler ways to create a multicolumn list is to define a fixed width for the <li> tag and float the list elements across the page. You can define the number of columns by setting the width of the <ul> or <ol> tag. The technique, shown in Figure A, is simple and effective, provided you can live with a couple of peculiarities.

The first peculiarity is the order of the list items. They flow from left to right across the page, and then drop down to fill in the next row. That's counter to the normal tendency to read down a column before moving to the top of the next column. The way the list items are arranged on the page may not be significant if the list order is completely arbitrary to begin with, but many lists have some intrinsic order, even if it's just an alphabetical arrangement.

Floated list items also suffer from browser-compatibility issues, and, of course, the problem browser is Internet Explorer. For some reason, IE omits the bullets in unordered lists and numbers every item in ordered lists with the number one. Fortunately, this isn't a problem for the many lists that don't need bullets or numbers. If it's a problem for your list, you can use one of the other techniques.

One of the advantages of the floated list item technique is that it requires no change to the normal list markup. For example, here's the XHTML markup for the list shown in Figure A:

<ul>
  <li>Item one</li>
  <li>Item two</li>
  <li>Item three</li>
  <li>Item four</li>
  <li>Item five</li>
  <li>Item six</li>
  <li>Item seven</li>
  <li>Item eight</li>
  <li>Item nine</li>
  <li>Item ten</li>
  <li>Item eleven</li>
  <li>Item twelve</li>
</ul>

And here's the CSS that turns that standard list into multiple columns:

ul {
    width: 700px;
    list-style-type:none;
}
li {
    width:180px;
    margin:15px 0 0 0;
    padding:0 10px 0 0;
    line-height:15px;
    float:left;
}

Note that the width:700px rule in the ul style sets the maximum width of the list to a little more than three times the width of the individual list elements (as set by the width:180px and margin:15px 0 0 0 rules in the li style), thus creating three columns. The list-style-type:none rule in the ul style removes the bullets from the list items in other browsers so the list matches the way it appears in IE. The key to this technique is the float:left rule in the li style. That's what causes the list items to be arranged in rows across the browser window until they fill the width of the parent element (the <ul> tag).

Technique 2: Manipulating margins

Another way to create a multicolumn list effect is to use margin settings to push some list items to the right and up to form the second and third columns as shown in Figure B. This technique has the advantage of keeping the list items arranged in columns that read down before going to the top of the next column. However, the technique requires quite a bit of extra markup and CSS code to make it work. Also, although there's no problem with disappearing bullets, this technique has its own browser compatibility issue with IE. In this case, the problem is that IE doesn't want to recognize hyperlinks in the first two columns. Fortunately, there's an easy, one-line hack that fixes the problem.

Here's the XHTML markup for the multicolumn list example in Figure B. Note the classes used to define the three columns, and the additional classes applied to the first list item in the second and third columns.

<ul>
  <li class="licol-1">Item one</li>
  <li class="licol-1">Item two</li>
  <li class="licol-1">Item three</li>
  <li class="licol-1">Item four</li>
  <li class="licol-2 licol-first">Item five</li>
  <li class="licol-2">Item six</li>
  <li class="licol-2">Item seven</li>
  <li class="licol-2">Item eight</li>
  <li class="licol-3 licol-first">Item nine</li>
  <li class="licol-3">Item ten</li>
  <li class="licol-3">Item eleven</li>
  <li class="licol-3">Item twelve</li>
</ul>

The CSS styles for this technique aren't terribly complicated, but the code does start getting longer as it defines all of those added classes.

li {
    width:180px;
    margin:15px 0 0 0;
    padding:0 10px 0 0;
    line-height:15px;
    position:relative; /* IE needs this in order to recognize links in all
 columns */
}
.licol-1 {
    margin-left:25px;
}
.licol-2 {
    margin-left:250px;
}    
.licol-3 {
    margin-left:475px;
}
.licol-first {
    margin-top:-105px;
}

The li style starts by setting the column width (width:180px), and also the margin (margin:15px 0 0 0), padding (padding:0 10px 0 0), and line height (line-height:15px). The other techniques include similar rules, but it's especially important in this technique because we need to know the exact vertical space occupied by each column. The position:relative rule is included to fix IE's problem with hyperlinks being ignored in any column except the last one on the right. Why that particular rule fixes the problem makes about as much sense as the problem itself, but it works.

The .licol-1, .licol-2, and .licol-3 styles set the left margins for the respective columns. The trick to this technique is in the .licol-first style. That's what moves the second and third columns up so that they're aligned side-by-side across the page instead of being a series of stair steps down the page. Specifying a negative top margin (margin-top:-105px) moves the column up above its natural starting place immediately below the previous list item. To get the top of the column to align with the previous column, you need to calculate the height of the previous column. In this case, there are four lines and three spaces, each measuring 15px, so the vertical offset is 105px (7 x 15 = 105). Because both the first and second columns of this example are the same height, I was able to use one class to define the vertical offset for both the second and third columns. If those columns were different heights, you'd need separate styles to set the vertical offset for each column.

The margin manipulation technique works for simple multicolumn lists, but it can be cumbersome to build (and even more cumbersome to maintain) for lists that are even slightly complicated or require editing and updating.

Technique 3: Divvying up with divs

The third multicolumn list technique takes a brute force approach to the challenge—it breaks a long list into shorter segments, makes each one a separate list, and places each list in a div. Then you can arrange the divs into columns by defining their widths and floating them across the page. It's not the most elegant approach, but as Figure C shows, it keeps list items in their normal order and can easily accommodate fluctuations in column height.

Here's the XHTML markup for the multicolumn list example in Figure C.

<div class="licol">
  <ol>
    <li>Let me not to the marriage of true minds</li>
    <li>Admit impediments; love is not love</li>
    <li>Which alters when it alteration finds</li>
    <li>Or bends with the remover to remove</li>
  </ol>
</div>
<div class="licol">
  <ol start="5">
    <li>Oh, no, it is an ever fixed mark</li>
    <li>That looks on tempests and is never shaken;</li>
    <li>It is the star to every wand'ring bark</li>
    <li>Whose worth's unknown, although his height be taken</li>
  </ol>
</div>
<div class="licol">
  <ol start="9">
    <li>Love's not Time's fool, though rosy lips and cheeks</li>
    <li>Within his bending sickle's compass come;</li>
    <li>Love alters not with his brief hours and weeks</li>
    <li>But bears it out even to the edge of Doom</li>
  </ol>
</div>

Although the markup for this technique is more involved, the CSS styles are quite simple:

li {
    margin:15px 0 0 0;
    padding:0 10px 0 0;
    line-height:15px;
}
.licol {
    float: left;
    width: 200px;
}

Creating list columns by placing three separate lists within three separate divs may look like overkill. You might be tempted to stick with one long list in the markup and break it into three columns with <div> tags placed within the list. Although that will produce the expected result in most current browsers, it won't validate due to improper nesting, and the invalid code may not work in future browsers. Another option is to omit the divs and apply the .licol class directly to the three <ol> tags. Unfortunately, that creates problems with disappearing bullets and numbers in IE, which leaves the list-within-div approach as the most predictable cross-browser solution.

The CSS styles for this technique are the simplest of the lot. All you really need is the .licol style to set the width of the columns (width:200px) and float the divs (float: left). In this case, the li style is just for consistency with the other techniques—it doesn't affect the way the technique works.