HTML Tables - Column Groups

Chapter 32 26 mins

Learning outcomes:

  1. What is the <colgroup> element
  2. Examples of using <colgroup>
  3. What is the <col> element
  4. Examples of using <col>

Introduction

While working with tables in HTML, we often want to group multiple rows into a single structural unit to be able to style all of them easily at once.

For instance, we might want to group the first two rows of a table and then the subsequent three rows, giving the first group an orange background and the second group a pink one.

Such row grouping can easily be done using <thead>, <tbody>, and <tfoot>, depending on what we're trying to group. That is, if we're grouping data rows, we'd want to use <tbody>; if we're grouping all header rows together, we'd want to use <thead>; and so on.

But sometimes we also want to group multiple columns together into one single unit to be able to apply styles to them easily.

The problem with this, however, is that we can NOT do so in the normal sense — each subsequent entry in a given column lives in different <tr> element and therefore, we can't obviously group all the entries into one single unit in the HTML source code.

So now what?

Well, no need to worry, for the designers of HTML long thought of one possible solution to this — to define the column structure separately, at the very start of a table. In this regard, we leverage the <colgroup> and <col> elements in HTML.

This chapter expands upon the purpose and application of these elements. Let's begin.

The <colgroup> element

We'll start with the <colgroup> element because that's where the interest lies, at least more of it.

The <colgroup> element is used to define a group of columns in a <table> element.

Clearly indicated by the HTML specification is the fact that <colgroup> must come prior to any row-group elements (<tbody>, <tbody>, and <tfoot>).

This is reiterated below:

[colgroup must appear] As a child of a table element, after any caption elements and before any thead, tbody, tfoot, and tr elements.

Now, since all the concrete data of an entire column in an HTML table is aggregately defined by disparate <th> or <td> elements inside <tr> elements, and not by <colgroup>, <colgroup> isn't allowed to contain any such concrete data.

As a matter of fact, the only possible content that we can put inside a <colgroup> is one or more <col> elements (more on that below), or just leave it empty.

The span attribute of <colgroup> is a particularly interesting feature of the element. It helps us define the number of columns that the element spans.

For example, if we want to define a group of three columns, we'll create a <colgroup> and then set its span attribute to "3". This just instructs the browser to make a group spanning three columns.

As stated earlier and stating it once again, <colgroup> doesn't contain any concrete column data in it; it's only meant to act as an ancillary element defining columns that are implicitly created by the following table data and header cells.

This ancillary support typically extends into an ease of styling given columns in the underlying table. That is, using <colgroup>, we can easily style a given column or a set of contiguous columns without having to go and manually style each <td> or <th> involved.

All this sounds too complex? Well, let's consider some examples to help clarify it all.

Examples of using <colgroup>

Recall the table from the previous chapter showcasing a bunch of programming languages and their paradigms?

In the following code, we have this same table, currently without any <colgroup>:

<table border="1">
   <thead>
      <tr>
         <th rowspan="2">Programming language</th>
         <th colspan="3">Paradigms</th>
      </tr>
      <tr>
         <th>Procedural</th>
         <th>Object-oriented</th>
         <th>Functional</th>
      </tr>
   </thead>
   <tr>
      <td>JavaScript</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
   <tr>
      <td>Java</td>
      <td>-</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
</table>
Programming languageParadigms
ProceduralObject-orientedFunctional
JavaScriptYesYesYes
Java-YesYes
C++YesYesYes

What we want to do is to give the first column a yellow background.

So how do we do so?

One viable option, that many naive HTML developers who don't know about <colgroup> might utilize, is to manually style each involved cell.

This is demonstrated as follows:

<table border="1">
   <thead>
      <tr>
         <th class="bg-yellow" rowspan="2">Programming language</th>
         <th colspan="3">Paradigms</th>
      </tr>
      <tr>
         <th>Procedural</th>
         <th>Object-oriented</th>
         <th>Functional</th>
      </tr>
   </thead>
   <tr>
      <td class="bg-yellow">JavaScript</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
   <tr>
      <td class="bg-yellow">Java</td>
      <td>-</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
</table>

A class is given to each and every element in the first column and then a background color style is set on this class using some basic CSS:

.bg-yellow {
   background-color: yellow;
}

Here's how the table looks after applying the CSS:

Programming languageParadigms
ProceduralObject-orientedFunctional
JavaScriptYesYesYes
Java-YesYes

However, as you'd agree, just to be able to style a given column to have a particular background, this is too much work.

What if we had a table with 10 columns and we wanted to have a different background for each and every single one of the columns?

Would it be wise enough to go on and manually give different classes to cell elements in every single column? Certainly not.

What we really need is the <colgroup> element here. The reason of using a <colgroup> element is because we want to be able to define a group of two columns, in this case, the 'Language' and 'Release date' columns.

Anyways, following is the code to accomplish the task of giving the first column a yellow background color:

<table border="1">
   <thead>
<colgroup class="bg-color"></colgroup>
<tr> <th rowspan="2">Programming language</th> <th colspan="3">Paradigms</th> </tr> <tr> <th>Procedural</th> <th>Object-oriented</th> <th>Functional</th> </tr> </thead> <tr> <td>JavaScript</td> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> <tr> <td>Java</td> <td>-</td> <td>Yes</td> <td>Yes</td> </tr> </table>

We create a <colgroup> element right before <thead> and then apply the .bg-yellow class to it. By default, <colgroup> represents a single column in the underlying table.

The result? Well, it's just amazing:

Programming languageParadigms
ProceduralObject-orientedFunctional
JavaScriptYesYesYes
Java-YesYes

By leveraging <colgroup>, we're able to style a column without having to manually select each and every involved cell therein.

It might seem a little bit counter-intuitive to have a <colgroup> represent one column because one column isn't really a 'group' of columns; and so, a <colgroup> isn't really a good idea here. Unfortunately, this is how the specification has been defined and we can't do much, if anything, about it.

Now what if we want to style two columns together instead of one as we did above? Well, we can use the span attribute to help us here.

First, let's augment the table above by adding a new column to it, for the year in which the respective programming was released:

<table border="1">
   <thead>
      <tr>
         <th rowspan="2">Programming language</th>
<th rowspan="2">Release year</th> <th colspan="3">Paradigms</th> </tr> <tr> <th>Procedural</th> <th>Object-oriented</th> <th>Functional</th> </tr> </thead> <tr> <td>JavaScript</td>
<td>1995</td> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> <tr> <td>Java</td>
<td>1995</td> <td>-</td> <td>Yes</td> <td>Yes</td> </tr> </table>
Programming languageRelease yearParadigms
ProceduralObject-orientedFunctional
JavaScript1995YesYesYes
Java1995-YesYes

Now, let's add a <colgroup> element in the table to style the first two columns, 'Programming language' and 'Release year':

<table border="1">
<colgroup span="2" class="bg-yellow"></colgroup>
<thead> <tr> <th rowspan="2">Programming language</th> <th rowspan="2">Release year</th> <th colspan="3">Paradigms</th> </tr> <tr> <th>Procedural</th> <th>Object-oriented</th> <th>Functional</th> </tr> </thead> <tr> <td>JavaScript</td> <td>1995</td> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> <tr> <td>Java</td> <td>1995</td> <td>-</td> <td>Yes</td> <td>Yes</td> </tr> </table>

Notice the <span> attribute's value here — it's set to "2" which means that the <colgroup> spans a total of 2 columns in the table.

Programming languageRelease yearParadigms
ProceduralObject-orientedFunctional
JavaScript1995YesYesYes
Java1995-YesYes

So what do you make of this? Easy or difficult?

The <col> element

The <col> element is used to define one or a set of multiple individual columns in a <colgroup> element.

The <col> element is used to represent an individual column or a set of columns inside a <colgroup> element in a table.

The <col> element is a void element. This makes perfect sense because there really is nothing to put inside a <col> unlike a <colgroup> (which can contain <col> elements in it).

Following the footsteps of <colgroup>, <col> can be used to easily style individual columns or a set of multiple, contiguous columns.

Moreover, just like <colgroup>, a <col> element can also have a span attribute to define its column span, i.e. the number of columns it consumes.

When the span attribute isn't specified on a <col> element, it defaults to 1.

But why would we want to have span on <col>?

It doesn't make much sense, if at all; we already have span on <colgroup> so it should cover everything that a span on <col> covers. Isn't that so?

Well, not quite so.

Yes, it surely might get you confused that the span attribute is available on both <colgroup> and <col> but there is one better way to think of this attribute to help us get past this confusion.

It's discussed as follows.

Making sense of the span attribute on <col>

The span attribute on a <colgroup> makes perfect sense; after all, if we know how many columns a group ought to contain, we can simply go on and set the span attribute. Right?

However, let's agree to the fact that span doesn't make much sense on a <col> element. Or does it?

Well, at the time of this writing, the latest HTML specification doesn't really expand upon the intuition behind the span attribute on <colgroup> or <col> and so we're left to make sense of it on our own.

Fortunately, there's quite a sensible way to think about it.

Instead of thinking of span on a <col> element as defining the number of columns it spans, think of it as a quantifier, specifying the number of times the <col> element should be repeated.

For example, consider the following code, assuming that it's part of a containing <table>:

<colgroup>
   <col span="5">
</colgroup>

Here we have span="5" set on a <col> element inside a <colgroup>, which is trying to say to repeat this <col> a total of 5 times, as follows:

<colgroup>
   <col>
   <col>
   <col>
   <col>
   <col>
</colgroup>

This way of thinking of the span attribute on a <col> element, as specifying the number of times to repeat the element, is far more sensible and intuitive than thinking of it as specifying the number of columns to span.

Obviously, with this quantification analogy, the following code would make sense as well:

<colgroup>
   <col class="bg-yellow" span="4">
   <col style="background-color: pink">
</colgroup>

The first <col> means to have 4 such elements repeated one after another, while the second <col> just refers to one, single column.

Think of this code being treated as follows:

<colgroup>
   <col class="bg-yellow">
   <col class="bg-yellow">
   <col class="bg-yellow">
   <col class="bg-yellow">
   <col style="background-color: pink">
</colgroup>

Making sense of the span attribute on <colgroup>

It's worthwhile mentioning here that this quantification analogy can also be extended to encompass the span attribute of a <colgroup> element. How?

Well, span on <colgroup> could be thought of as specifying the number of times to repeat a given <col> inside of it.

Henceforth, the following HTML:

<colgroup class="bg-yellow" span="4"></colgroup>

is really trying to say this:

<colgroup>
   <col class="bg-yellow">
   <col class="bg-yellow">
   <col class="bg-yellow">
   <col class="bg-yellow">
</colgroup>

This also means that a <colgroup> with a given span is basically the same as a <colgroup> with one <col> with an identical span.

Thus, the following HTML with a <colgroup> only:

<colgroup class="bg-yellow" span="4"></colgroup>

is the same as this one with a single <col> inside the <colgroup>:

<colgroup>
   <col class="bg-yellow" span="4">
</colgroup>

So what do you say? Helpful analogy or not?

Examples of using <col>

As with <colgroup>, let's now consider a few examples of <col> to help us comprehend it better.

We'll use the same programming language table example that we used in the discussion above, restated as follows:

<table border="1">
   <thead>
      <tr>
         <th rowspan="2">Programming language</th>
         <th rowspan="2">Release year</th>
         <th colspan="3">Paradigms</th>
      </tr>
      <tr>
         <th>Procedural</th>
         <th>Object-oriented</th>
         <th>Functional</th>
      </tr>
   </thead>
   <tr>
      <td>JavaScript</td>
      <td>1995</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
   <tr>
      <td>Java</td>
      <td>1995</td>
      <td>-</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
</table>
Programming languageRelease yearParadigms
ProceduralObject-orientedFunctional
JavaScript1995YesYesYes
Java1995-YesYes

The idea is also the same: to style the first column by giving it a yellow background color.

Using the <col> element, this is done as follows (supposing that the .bg-yellow class contains the background color style in it, as shown above):

<table border="1">
   <colgroup>
<col class="bg-yellow">
</colgroup> <thead> <tr> <th rowspan="2">Programming language</th> <th rowspan="2">Release year</th> <th colspan="3">Paradigms</th> </tr> <tr> <th>Procedural</th> <th>Object-oriented</th> <th>Functional</th> </tr> </thead> <tr> <td>JavaScript</td> <td>1995</td> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> <tr> <td>Java</td> <td>1995</td> <td>-</td> <td>Yes</td> <td>Yes</td> </tr> </table>
Programming languageRelease yearParadigms
ProceduralObject-orientedFunctional
JavaScript1995YesYesYes
Java1995-YesYes

Perfect.

Let's now style the first two columns using <col>, leveraging its span attribute:

<table border="1">
   <colgroup>
      <col span="2" class="bg-yellow">
   </colgroup>
   <thead>
      <tr>
         <th rowspan="2">Programming language</th>
         <th rowspan="2">Release year</th>
         <th colspan="3">Paradigms</th>
      </tr>
      <tr>
         <th>Procedural</th>
         <th>Object-oriented</th>
         <th>Functional</th>
      </tr>
   </thead>
   <tr>
      <td>JavaScript</td>
      <td>1995</td>
      <td>Yes</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
   <tr>
      <td>Java</td>
      <td>1995</td>
      <td>-</td>
      <td>Yes</td>
      <td>Yes</td>
   </tr>
</table>
Programming languageRelease yearParadigms
ProceduralObject-orientedFunctional
JavaScript1995YesYesYes
Java1995-YesYes

Simple, yet again.

Things to note

It's now time to note a couple of important points regarding the <colgroup> and <col> elements.

<colgroup> and <col> don't define actual column data

Despite the fact that we've discussed it twice before, it's so important to note this fact regarding <colgroup> and <col> that we feel it's necessary to re-iterate on it.

So hear this out once more: the <colgroup> and <col> elements are NOT intended to define concrete data of any column in a table. They are exclusively meant to be used as supporting elements, to complement the structure formed by actual table cells (<th> and <td>).

<colgroup> can only contain <col>s, or be empty. Similarly, <col> can't contain anything, for it's a void element.

The result: <colgroup> can have absolutely NO actual table data in it. Period.

Purely by reading the names of these elements, one might assume that they are used for defining columns within a table, and while that is true, it's crucial to keep in mind that these columns are only intended for structural and stylistic benefits more than anything else.

The starting tag of <colgroup> can be omitted

Across the web, examples containing <col> elements without parent <colgroup>s are quite common.

Here's an example (teaser: this is completely valid HTML):

<table>
<col> <col> <colgroup span="3"></colgroup>
<thead> <tr> <th rowspan="2">Programming language</th> <th rowspan="2">Release year</th> <th colspan="3">Paradigms</th> </tr> <tr> <th>Procedural</th> <th>Object-oriented</th> <th>Functional</th> </tr> </thead> <tr> <td>JavaScript</td> <td>1995</td> <td>Yes</td> <td>Yes</td> <td>Yes</td> </tr> <tr> <td>Java</td> <td>1995</td> <td>-</td> <td>Yes</td> <td>Yes</td> </tr> </table>

This might incorrectly suggest to us that <col> can be nested directly inside a <table> element in addition to being nested inside a <colgroup>. However, this is far from the truth!

As clearly labeled in the HTML spec, a <col> element can come inside, and only inside, a <colgroup>.

But then what about the code above, given that it's perfectly valid?

Now this has to do with some subtle details conveyed in the HTML spec. Specifically, if we read the section on the technical summary of <colgroup>, we come across the following:

A colgroup element's start tag can be omitted if the first thing inside the colgroup element is a col element, and if the element is not immediately preceded by another colgroup element whose end tag has been omitted.

This means that the <colgroup> element's start tag can be omitted when it isn't preceded by another <colgroup> whose ending tag has been omitted — a technical way of specifying an obvious thing.

Reading this, if we now review the code above, we see that even though <colgroup> isn't explicitly mentioned in the code, the first and second <col> elements are both part of an implicitly-created <colgroup>.

Here's how the browser treats the code:

<table>
<colgroup>
<col> <col>
</colgroup>
<colgroup span="3"></colgroup> ... </table>

It automatically creates a <colgroup> to encapsulate the set of two <col>s at the start of the table.

Best of all, we can even confirm this implicit <colgroup> creation by the browser by inspecting the HTML document.

Here's the HTML DOM view in the Developer's Tool for the table above:

Implicit addition of <colgroup>
Implicit addition of <colgroup>

Notice how the browser has automatically added in a <colgroup> and put both the <col> elements inside this <colgroup>.

This is because, as per the HTML spec, the starting as well the ending tag of the <colgroup> element has been omitted here but the browser is capable enough of detecting this and adding in the element itself.

A <colgroup> with a span attribute can't contain any <col>

If a <colgroup> element contains a span attribute, then it can NOT contain anything inside of it — no <col> elements.

This is a good design decision on the HTML standard's end, for if it was the other way round, possible to have content inside a <colgroup> in addition to its span attribute, there would've been ambiguity in what to consider for the length of the column group: the content or the span attribute.

Likewise, the following HTML is invalid:

<colgroup span="2">
   <col>
</colgroup>

We have a <colgroup> with both a span and <col> elements inside it.

The correct choice is to either use span, as follows:

<colgroup span="2"></colgroup>

or not use with and just stick to using individual <col> elements inside the <colgroup>, as follows:

<colgroup>
   <col>
</colgroup>