HTML Tables - Header Scope

Chapter 34 19 mins

Learning outcomes:

  1. The scope attribute of <th> elements
  2. The purpose of scope on <th>
  3. The main four values — row, col, rowgroup and colgroup
  4. Things to note regarding scope

Introduction

So far in this unit, we've explored a solid amount of concepts underpinning HTML tables.

In particular, we've gone through the very basics of setting up tables using the elements <table>, <tr>, <th>, and <td>, and then how to section tables in different groups of rows, using <thead>, <tbody>, and <tfoot>.

Not only this, but in the last chapter, we also learnt about the <colgroup> and <col> elements and that how we can use them to style entire columns of a table at once.

We're nearing the completion of this unit, and in this chapter we'll get even closer to that point once we're done with understanding the intuition behind the scope attribute of <th> elements, used to specify the scope of given header cells in a table.

The scope attribute of <th>

Recall the <th> element from the HTML Tables — Basics chapter? It's used to define a header cell in a <table> element.

Now, there is a particularly important attribute of <th> that is often used to explicitly specify its scope in a table, i.e. which set of data cells the header applies to. This attribute is called scope.

The scope attribute of a <th> element is used to specify its scope, that is, whether the header applies to an entire row, or column, etc.

Quoting from the official HTML standard, the scope attribute of a <th>:

Specifies which cells the header cell applies to

The five possible values of scope are as follows:

  • row — the header cell applies to the subsequent cells in the underlying row.
  • col — the header cell applies to the subsequent cells in the underlying column.
  • rowgroup — the header cell applies to the subsequent cells in the underlying group of rows.
  • colgroup — the header cell applies to the subsequent cells in the underlying group of columns
  • auto — the scope is determined based on context. This is the default value in case scope is omitted from a <th>.

Let's dive deeper into each of the starting four values here to understand what scope has to offer us...

The row and col scopes

The simpler of the four scope values are row and col, the latter being the simplest.

The row scope of a <th> element means that the header applies to the entire set of subsequent cells in its respective row.

Such a scope is common when we have row headers as opposed to the column headers, which is the more typical usage of headers. We'll see an example shortly below.

On the same lines,

The col scope of a <th> element means that the header applies to the entire set of subsequent cells in its respective column.

Let's consider an example to help understand both the row and col scopes.

Say we have to represent the following table using HTML, showcasing the available time slots on weekdays for a meeting with the CEO of a company:

MonTueWedThuFri
12:00 - 12:30Available
12:30 - 13:00AvailableAvailable
13:00 - 13:30Available
13:30 - 14:00AvailableAvailable
14:00 - 14:30Available

Notice that there are precisely two different kinds of headers: the ones for the weekdays are column headers, governing their entire columns, whereas the ones for the time slots are row headers, governing their entire rows.

Here's the HTML to replicate this table's content and structure:

<table border="1">
   <thead>
      <tr>
         <td> </td>
         <th>Mon</th>
         <th>Tue</th>
         <th>Wed</th>
         <th>Thu</th>
         <th>Fri</th>
      </tr>
   </thead>
   <tr>
      <th>12:00 - 12:30</th>
      <td>Available</td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
   </tr>
   <tr>
      <th>12:30 - 13:00</th>
      <td></td>
      <td></td>
      <td>Available</td>
      <td>Available</td>
      <td></td>
   </tr>
   <tr>
      <th>13:00 - 13:30</th>
      <td></td>
      <td></td>
      <td>Available</td>
      <td></td>
      <td></td>
   </tr>
   <tr>
      <th>13:30 - 14:00</th>
      <td>Available</td>
      <td></td>
      <td></td>
      <td></td>
      <td>Available</td>
   </tr>
   <tr>
      <th>14:00 - 14:30</th>
      <td>Available</td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
   </tr>
</table>
   
MonTueWedThuFri
12:00 - 12:30Available
12:30 - 13:00AvailableAvailable
13:00 - 13:30Available
13:30 - 14:00AvailableAvailable
14:00 - 14:30Available

Evidently, as of now, there is no scope attribute set on any <th> element above, and honestly speaking, it isn't even needed in such simple cases where we have the headers at the very top and very left of a table.

However, if we want to be explicit regarding our header cells' scopes, we can definitely sprinkle scope on them. This is done below:

<table border="1">
   <thead>
      <tr>
         <td> </td>
         <th scope="col">Mon</th>
         <th scope="col">Tue</th>
         <th scope="col">Wed</th>
         <th scope="col">Thu</th>
         <th scope="col">Fri</th>
      </tr>
   </thead>
   <tr>
      <th>12:00 - 12:30</th>
      <td scope="row">Available</td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
   </tr>
   <tr>
      <th scope="row">12:30 - 13:00</th>
      <td></td>
      <td></td>
      <td>Available</td>
      <td>Available</td>
      <td></td>
   </tr>
   <tr>
      <th scope="row">13:00 - 13:30</th>
      <td></td>
      <td></td>
      <td>Available</td>
      <td></td>
      <td></td>
   </tr>
   <tr>
      <th scope="row">13:30 - 14:00</th>
      <td>Available</td>
      <td></td>
      <td></td>
      <td></td>
      <td>Available</td>
   </tr>
   <tr>
      <th scope="row">14:00 - 14:30</th>
      <td>Available</td>
      <td></td>
      <td></td>
      <td></td>
      <td></td>
   </tr>
</table>
   

The weekday header cells get scope="col" since they apply to the subsequent data cells (<td> elements) in their respective columns. Similarly, the time slot header cells get scope="row" as they apply to the subsequent data cells in their respective rows.

As stated earlier, adding in scope has absolutely NO effect on the styling of the header cells; it's only meant for accessibility and better explaining the structure of a table.

Following is the output we get with this addition of scope:

MonTueWedThuFri
12:00 - 12:30Available
12:30 - 13:00AvailableAvailable
13:00 - 13:30Available
13:30 - 14:00AvailableAvailable
14:00 - 14:30Available

Quite obviously, there's no visual dissimilarity between this table and the one shown previously without scope.

The rowgroup scope

Where the row scope applies to a single column, the rowgroup scope applies to an entire group of columns.

The rowgroup scope of a <th> element means that the header cell applies to the entire set of subsequent rows in the containing row group.

Recall that a group of rows in a table is denoted via the sectioning elements <thead>, <tbody> and <tfoot>.

Thus, ideally, the scope value rowgroup should only be used when the respective <th> element is nested within either of these elements and is, likewise, a part of a row group.

As a matter of fact, the HTML standard says that rowgroup must only be used when the <th> element is part of a row group (i.e. <thead>, <tbody>, or <tfoot>).

Time for an example.

Recall the following table back from the HTML Tables — Table Sections chapter:

Programming languageRelease year
Object-oriented
JavaScript1995
Java1995
C++1985
Procedural
C1972
Pascal1970

We have a list of five programming languages along with their release year, grouped according to their paradigm, which is displayed at the top of every group.

The headers reading 'Object-oriented' and 'Procedural' both apply to the entire set of rows following them, within their containing <tbody> element. This is the perfect use case of the rowgroup scope on a header cell.

In the following example, we set scope="rowgroup" on both these headers:

<table border="1">
   <thead>
      <tr>
         <th>Programming language</th>
         <th>Release year</th>
      </tr>
   </thead>
   <tbody>
      <tr>
         <th scope="rowgroup" colspan="2">Object-oriented</th>
      </tr>
      <tr>
         <td>JavaScript</td>
         <td>1995</td>
      </tr>
      <tr>
         <td>Java</td>
         <td>1995</td>
      </tr>
      <tr>
         <td>C++</td>
         <td>1985</td>
      </tr>
   </tbody>
   <tbody>
      <tr>
         <th scope="rowgroup" colspan="2">Procedural</th>
      </tr>
      <tr>
         <td>C</td>
         <td>1972</td>
      </tr>
      <tr>
         <td>Pascal</td>
         <td>1970</td>
      </tr>
   </tbody>
</table>

Again, visually, there is no difference between this table and the one shown before — we get the exact same output:

Programming languageRelease year
Object-oriented
JavaScript1995
Java1995
C++1985
Procedural
C1972
Pascal1970

But at least now, more importantly than when we set scope="row" or scope="col", thanks to scope="rowgroup", we have clearly labeled that the 'Object-oriented' and 'Procedural' headers apply to the entire subsequent set of rows in the containing <tbody> element.

Elaborating on this a little more, the 'Object-oriented' header applies to the entire group colored pink whereas the 'Procedural' header applies to the one colored yellow:

Programming languageRelease year
Object-oriented
JavaScript1995
Java1995
C++1985
Procedural
C1972
Pascal1970

Simple, isn't this?

Now, let's see the fourth and last value of the scope attribute in this series — colgroup.

The colgroup scope

Just how rowgroup extends a header's scope to the entire containing row grouping element, the colgroup scope does so albeit for the entire containing column grouping element.

Do you recall which element serves to group a set of columns together? It's <colgroup>, as we learnt in the previous chapter, HTML Tables — Column Groups:

The colgroup scope of a <th> element means that the header cell applies to the entire set of columns in the underlying column group.

As with rowgroup, the colgroup scope should ideally only be used on a <th> element when it's part of a <colgroup> element.

Once again, this is a requirement as per the current HTML standard, i.e. to have a <th> on which we wish to set scope="colgroup" be a part of a <colgroup>.

Let's see an example, one that we've already seen from the previous chapters.

Recall the following table from the HTML Tables — Table Sections chapter:

Programming languageParadigms
ProceduralObject-orientedFunctional
JavaScriptYesYesYes
Java-YesYes
C++YesYesYes

Carefully notice the 'Paradigms' header, governing further three headers beneath it: 'Procedural', 'Object-oriented', and 'Functional'.

The 'Paradigms' header here is a perfect use case for scope="colgroup". This is because it ought to govern the data cells not just in one column but in all three columns.

So firstly, let's add in the desired amount of <colgroup> elements to this table to create two column groups, one for 'Programming language' and one for 'Paradigms':

<table border="1">
<colgroup></colgroup> <colgroup span="3"></colgroup>
<thead> <tr> <th rowspan="2">Programming language</th> <th colspan="3">Paradigms</th> </tr> ... </table>

Now, with the column group structure in place, let's apply scope="colgroup" to the 'Paradigms' header cell:

<table border="1">
   <colgroup></colgroup>
   <colgroup span="3"></colgroup>
   <thead>
      <tr>
         <th rowspan="2">Programming language</th>
         <th scope="colgroup" colspan="3">Paradigms</th>
      </tr>
   ...
</table>

Here's the result we get:

Programming languageParadigms
ProceduralObject-orientedFunctional
JavaScriptYesYesYes
Java-YesYes
C++YesYesYes

Needless to say, there's absolutely nothing visually different in this table than what we had above.

Following the footsteps of the previous section, in the example below we illustrate both the column groups to which the given headers apply — 'Programming language' goes with the one colored pink while 'Paradigms' goes with the one colored yellow:

Programming languageParadigms
ProceduralObject-orientedFunctional
JavaScriptYesYesYes
Java-YesYes
C++YesYesYes

Things to note

Not all header cells require scope

One question that might pop in your mind at this stage is that: Is scope required on every <th> element or not?

Fortunately enough, no!

The scope attribute is NOT required on every single <th> element in an HTML table.

To be specific, when a <th> element occurs

  • in the very first row of a table, which is typically always the row of headers of the table, or
  • in the very first column of a table, which is typically always the column of headers of the table,

there's really no need of adding scope on the <th>, for the browser (with the default scope="auto" setting) can easily determine the scope on its own.

Likewise, the following is not really required:

<table>
   <tr>
      <th scope="col">Programming language</th>
      <th scope="col">Release year</th>
   </tr>
   <tr>
      <td>JavaScript</td>
      <td>1995</td>
   </tr>
   <tr>
      <td>Java</td>
      <td>1995</td>
   </tr>
   <tr>
      <td>C++</td>
      <td>1985</td>
   </tr>
</table>

We're completely OK in reducing this HTML down to the following, letting go off the scope="col" attribute from the headers:

<table>
   <tr>
      <th>Programming language</th>
      <th>Release year</th>
   </tr>
   <tr>
      <td>JavaScript</td>
      <td>1995</td>
   </tr>
   <tr>
      <td>Java</td>
      <td>1995</td>
   </tr>
   <tr>
      <td>C++</td>
      <td>1985</td>
   </tr>
</table>

It's only when the <th>s appear in positions apart from the ones given above that we need to think about adding scope to them in order to be explicit about which data cells do they apply to.

scope doesn't have any stylistic effect

Although, we have seen this point in action a handful of times above, it's important to re-emphasize on it.

The scope attribute is NOT meant to produce any stylistic effect in a table; it's rather exclusively meant to make a table more accessible for screen readers by making it clear as to which data cells (<td>s) correspond to which header, or set of headers.

Thus, no matter how many scopes we have in a table, it'll be displayed the exact same way with or without them.

Further reading

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage