1

Given a table like this: enter image description here

(HTML-code):

<table>
  <thead>
    <tr>
      <th>H1-A</th>
      <th colspan="3">H1-B</th>
      <th colspan="3">H1-C</th>
    </tr>
    <tr>
      <th>H2-A</th>
      <th colspan="2">H2-B</th>
      <th>H2-C</th>
      <th colspan="3">H2-D</th>
      <th colspan="3">H2-E</th>
    </tr>
    <tr>
      <th height="10">H3-A</th>
      <th>H3-B</th>
      <th>H3-C</th>
      <th>H3-D</th>
      <th>H3-E</th>
      <th>H3-F</th>
      <th>H3-G</th>
      <th>H3-I</th>
      <th>H3-J</th>
      <th>H3-K</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
    </tr>
    <tr>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
    </tr>
    <tr>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
      <td>data</td>
    </tr>
  </tbody>
</table>


Is it possible to use the colgroup-tag to mark the headers this table? Put another way:

Can colgroups be used for multi-row headers?

I need to mark the headers for accesibility purposes and it seems like this is the only option for such tables, given that the headers-attribute seems to have poor screenreader-support

TylerH
  • 20,799
  • 66
  • 75
  • 101
erlend97
  • 13
  • 1
  • 3
  • there is a slight error in your original HTML, `H1-A` should have `colspan="4"` added to it, I have corrected that in my answer but it is useful for anyone else trying to fix the table for you if the markup is corrected. – GrahamTheDev Jan 31 '21 at 14:45

2 Answers2

0

My understanding is that you can identify the column groups with “colgroup” elements, but you cannot specify this header structure with them, because “colgroup” elements cannot contain other “colgroup” elements.

My experimentation has led me to the conclusion that support by screen readers is worse than what you describe: Support is poor and varying not only for the “headers” attribute, but for tables as a whole, except the simplest ones, and it is not even uniformly good for the simplest tables.

It is also worse because sometimes screen readers do indeed announce headers for cells, and announce the wrong headers (when the table is properly marked up), causing users to believe that something belongs to one category when in fact it belongs to another.

If I want to deliver reliable understandability across the ability and assistive-technology spectra, I may render data as tables, but I render the same data again in some more widely supported (though less parsimonious) format, such as headings and paragraphs.

Jonathan Pool
  • 369
  • 1
  • 9
0

This isn't as straightforward as I thought it would be, I tried numerous techniques such as just typical colgroup and then I even tried the headers attribute, which allows you to define headers via an ID.

After a bit of searching I came across this example page where someone had tried 3 level column headers (example 3) and they experienced similar issues.

After a bit of hacking about I did manage to get something that is workable and seems OK but it would need a lot more testing in other screen readers as I only have NVDA and JAWS on my PC (and don't have time to test all the browser-screen reader combinations at the moment).

A possible solution

As with anything if I can't get semantic elements to work and can't get WAI-ARIA to work then I fall back to our old friends aria-hidden and visually hidden text.

What I do is completely hide the first two header rows in the table from screen readers and then use visually hidden text to add the information back into the 3rd level headers.

So for example for column 10 I add "H1-C, H2-E" before "H3-K".

<th scole="col"><span class="visually-hidden">H1-C, H2-E,</span>H3-K</th>

This way a screen reader user is effectively presented with a normal 10 column table which is easy to navigate, but all the relevant header information is read out.

For example the first cell is read as

row 2 H1-A, H2-A,H3-A column 1 data 1a

You would obviously want to build some solution (preferably on the server but with JS would be acceptable in a pinch) that does this automatically to avoid any mistakes being made.

It is not an ideal solution, but it is the best I can come up with and is what I would do as at least all the relevant information is presented to screen reader users.

Caveat: I would only use the solution if I could automate the process, if I could not automate generation of the visually hidden text for each column I would not do anything and leave the table as it is, due to the fact that a mistake here would likely be less accessible than just leaving the table alone.

Example

table{
    width: 100%;
}
th{
   text-align: center;
}
th, td{
    border: 1px solid #333;
}

.visually-hidden { 
    border: 0;
    padding: 0;
    margin: 0;
    position: absolute !important;
    height: 1px; 
    width: 1px;
    overflow: hidden;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
    clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
    clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
    white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<table>
  <thead>
    <tr aria-hidden="true">
      <th colspan="4">H1-A</th>
      <th colspan="3">H1-B</th>
      <th colspan="3">H1-C</th>
    </tr>
    <tr aria-hidden="true">
      <th>H2-A</th>
      <th colspan="2">H2-B</th>
      <th>H2-C</th>
      <th colspan="3">H2-D</th>
      <th colspan="3">H2-E</th>
    </tr>
    <tr>
      <th scole="col" height="10"><span class="visually-hidden">H1-A, H2-A,</span>H3-A</th>
      <th scole="col"><span class="visually-hidden">H1-A, H2-B,</span>H3-B</th>
      <th scole="col"><span class="visually-hidden">H1-A, H2-B,</span>H3-C</th>
      <th scole="col"><span class="visually-hidden">H1-A, H2-C,</span>H3-D</th>
      <th scole="col"><span class="visually-hidden">H1-B, H2-D,</span>H3-E</th>
      <th scole="col"><span class="visually-hidden">H1-B, H2-D,</span>H3-F</th>
      <th scole="col"><span class="visually-hidden">H1-B, H2-D,</span>H3-G</th>
      <th scole="col"><span class="visually-hidden">H1-C, H2-E,</span>H3-I</th>
      <th scole="col"><span class="visually-hidden">H1-C, H2-E,</span>H3-J</th>
      <th scole="col"><span class="visually-hidden">H1-C, H2-E,</span>H3-K</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>data 1a</td>
      <td>data 2a</td>
      <td>data 3a</td>
      <td>data 4a</td>
      <td>data 5a</td>
      <td>data 6a</td>
      <td>data 7a</td>
      <td>data 8a</td>
      <td>data 9a</td>
      <td>data 10a</td>
    </tr>
    <tr>
      <td>data 1b</td>
      <td>data 2b</td>
      <td>data 3b</td>
      <td>data 4b</td>
      <td>data 5b</td>
      <td>data 6b</td>
      <td>data 7b</td>
      <td>data 8b</td>
      <td>data 9b</td>
      <td>data 10b</td>
    </tr>
  </tbody>
</table>
TylerH
  • 20,799
  • 66
  • 75
  • 101
GrahamTheDev
  • 22,724
  • 2
  • 32
  • 64