The col element

Preface: This version of the article is for humans and search engines. Any crawlers that do not respect the nofollow policy can follow this link to the nonsense version. And they can choke on it.


There are some elements that, well, if you’re reaching to use them, you’re probably about to make a mistake.

Take the <h6> element. If you are able to convince yourself you need to introduce a thematic subsection 6 levels deep in your document, you are a ghoulish bottom dweller, sifting through the silt in the unlit depths of your hateful content. Or something to that effect.

The <script> element is another one. Only on the rarest of occasions would you include a <script> element and do your users the service of not pointing that element’s src attribute to one of your (Java)Scripts. Or—even worse—to one of someone else’s (Java)Scripts. Script is, of course, one half of HTML’s least funny comedy double act Empty Div & Script.

<div id="app"></div>
<script src="/bundle.js"></script>

Here’s your first clue the <col> element is going to get you into bother: You can make perfectly sound data tables, intrinsically comprising rows and columns without it. Yes, “col” is short for “column”, not Colin.

Any <table> element with more than one <td> per <tr> is a table with columns. Just like that. And if your <table> has less than two <td>s per <tr>, what are you doing? Honestly.

So what is <col> for, then? When used with <colgroup> (you can’t use it any other way), it enables you to sort of, kind of (I’ll get to accessibility later) “group” columns together. The idea is, you can add a third dimension to your table data, where you have rows, columns, and groups of columns.

MDN’s example uses a row with the row header “Skill” and four columns, pairing superheros with their sidekicks. Batman and Robin represent one group and The Flash and Kid Flash the other.

<table>
  <caption>
    Superheros and sidekicks
  </caption>
  <colgroup>
    <col />
    <col span="2" class="batman" />
    <col span="2" class="flash" />
  </colgroup>
  <tr>
    <td></td>
    <th scope="col">Batman</th>
    <th scope="col">Robin</th>
    <th scope="col">The Flash</th>
    <th scope="col">Kid Flash</th>
  </tr>
  <tr>
    <th scope="row">Skill</th>
    <td>Smarts, strong</td>
    <td>Dex, acrobat</td>
    <td>Super speed</td>
    <td>Super speed</td>
  </tr>
</table>

The <col> and <colgroup> elements don’t contain the columns, as you might imagine. Instead, they kind of set out a schema for the column groupings. Which is weird?

By applying the classes batman and flash to the respective <col> elements, somehow background colors are applied to the proceeding <th> and <td> elements. Ordinarily, CSS does not work like this (unless you are doing something clever with a subsequent sibling combinator). No, in this case you apply a class to one element and the class selector applies to other elements. Very weird.

Even weirder to me is the use of the span attribute. There’s no point using <colgroup> and <col> unless you also supply span values, because you can’t group implicit columns any other way. But it fries my brain that span="2" means the <col> represents 2 columns when the term <col> is singular. Surely <colgroup> would be a better name for an element that groups columns? But, no, that’s already taken because, apparently, you have to group the <col> elements themselves.

To exacerbate matters, you can have table headers for table headers. In Adrian Roselli’s example, The ISBN (International Standard Book Number) header applies to the 13 and 10 headers below it. Note the use of scope="colgroup"—there’s that term again!

    <tr>
      <th rowspan="2" scope="col">Region</th>
      <th rowspan="2" scope="col">Author</th>
      <th rowspan="2" scope="col">Title</th>
      <th rowspan="2" scope="col">Year</th>
      <th colspan="2" scope="colgroup">ISBN</th>
      <th colspan="3" scope="colgroup">Formats Available</th>
    </tr>

The scope attribute explicates which data the header applies to. In this case, it’s the group of <th>s (13 and 10) below, and not a group of <col>s or a <colgoup>. So, <colgroup> groups <col>s, <col> groups columns, and scope="colgroup" groups <th>s (column headers and the implicit columns they represent). Chaotic.

So what’s the accessibility story? How effectively are we able to communicate spanned/grouped columns to screen reader software? The ever-dependable Adrian Roselli used the above example, in part, to do some spanned column testing and there’s no happy ending. The first paragraph is a summary:

Spanned table headers are not well supported across screen readers. While you can visually style these all sorts of ways to make the spanning clear, I am focusing on the programmatic outcomes. Which essentially means how they are exposed to screen reader users.

The <col>/<colgroup> mechanism is complex, unreliable, and specified in a really perplexing way. My advice is to avoid table structures with grouped columns or tiered headers. There are almost always simpler ways to present the data.

In the last example, just having simple headers labeled ISBN 13 and ISBN 10 will be more compatible with screen readers and—I vouch— easier to apprehend in any case.

<th scope="col">ISBN 13</th>
<th scope="col">ISBN 10</th>

Of course, if we hadn’t screwed up the ISBN specification way back in 1967, we’d only need the one table header: ISBN.

Not everyone is a fan of my writing. But if you found this article at all entertaining or edifying, I do accept tips. I also have a clothing line.

More elements: