Summary
Tables shouldn't be used for layout, as they can result in redundant information being announced as the user navigates through the document using a screen reader, such as "table with 3 columns and 4 rows column 1 row 2 ⋯". When nested tables are used, other redundant information is announced, such as "table nesting level 3 column 2 row 1 ⋯".
If tables are used for layout, then the WAI-ARIA presentation role can be used to remove the table semantics so the table information is no longer announced when the user navigates the page.
When controls are grouped in a fieldset
element with a legend
, the content of the legend element is announced by screen readers to provide context for the individual controls in the fieldset. This is particularly useful for controls like radio buttons where the group needs a label, as the individual labels for each radio button may not make sense on their own; for example, if the label is just "Yes" or "No". Unfortunately, if the form controls in the fieldset are in a layout table, then the legend is not announced as a label for the group in JAWS, even if the table has role="presentation"
.
Author: Gez Lemon
Layout tables
Although using role="presentation"
stops redundant table information being announced to screen reader users, form controls within a fieldset
are not reliably announced by JAWS with the context of the legend
element to provide a label for the group. In the following example, when a JAWS user navigates through the radio buttons, they hear something like, "radio button not checked maybe 1 of 3", "radio button not checked yes 2 of 3", and "radio button not checked no 3 of 3" without the context of "Attending" (Firefox with JAWS provides the context when navigating forwards to a radio button in the group, but not when navigating backwards to a radio button in the group. IE with JAWS completely ignores the context provided by the legend element).
If the layout table isn't included, a JAWS user hears "Attending" as the context for the radio button in the group that receives focus. Continuing to navigate through the radio buttons in the group only announces the radio button information, as the context has already been provided.
Repair techniques
When form controls are in a layout table within fieldset
/legend
elements, the context information is lost in JAWS, even when the table semantics are removed with role="presentation"
. The obvious solution is to remove the layout table, but sometimes this option is too costly and developers just want a solution that works. One solution is to use WAI-ARIA's aria-describedby attribute. To do this, add an id
attribute to the legend
element, and use aria-describedby
on the radio buttons that references the id
attribute set on the legend
element.
This technique works with JAWS, but requires using aria-describedby
for each element in the fieldset. This approach also has the undesirable effect of announcing the context after the radio button information, rather than before the radio button, as the user would be used to with regular elements within a fieldset
/legend
section. It also means that screen readers that do announce the group label provided by the legend element also announce the description afterwards, which is overly verbose.
User agent | Result |
---|---|
IE9 and IE10 with JAWS 13 | "Attending?" Announced after the radio button information is announced. |
IE9 and IE10 with NVDA | "Attending?" announced as the label for the group, and announced again as the long description after the radio button information is announced. |
Firefox 17 with JAWS 13 | Announced after the radio button information is announced. |
Firefox 17 with NVDA | "Attending?" announced as the label for the group, and announced again as the long description after the radio button information is announced. |
Ideally, we want a group label that is announced just once the first time the group is visited, so a more effective solution would be to use a role of group (or region if the section is significantly important) on the table element instead of the presentation
role, and use aria-labelledby to provide the name for the group. Steve Faulkner pointed out that child element roles are unaffected by a parent role of group
, so the table semantics are still present and need overriding with role="presentation"
. This should result in the same relationship as fieldset
/legend
without the layout table.
This arrangement works in Firefox, and ensures that the context is reliably provided before the radio button information just once when the group is visited forwards or backwards, in the same way as when the layout table isn't present with fieldset
/legend
. Unfortunately, it is completely ignored in IE with JAWS, and announced twice in browsers that do announce the legend
content as a label for the group.
User agent | Result |
---|---|
IE9 and IE10 with JAWS 13 | Group label not announced. |
IE9 and IE10 with NVDA | "Attending?" group label announced twice. |
Firefox 17 with JAWS 13 | Group label announced once. |
Firefox 17 with NVDA | "Attending?" group label announced twice. |
As this attempt used lots of role="presentation"
markup, I decided to wrap the table in a div
element and moved the role="group"
and aria-labelledby
markup to the containing div
element, and put the table role back to role="presentation"
. This arrangement didn't work either. After further discussion with Steve Faulkner, he pointed out that IE doesn't suppress the semantics of table cell elements when role="presentation"
is used on the table
element (it should do). I added role="presentation"
to each of the table cells, and it finally worked as expected.
User agent | Result |
---|---|
IE9 and IE10 with JAWS 13 | "Attending?" announced as the group label. |
IE9 and IE10 with NVDA | "Attending?" group label announced twice. |
Firefox 17 with JAWS 13 | "Attending?" announced as group label twice when navigating forwards, and one when navigating backwards in to the group. |
Firefox 17 with NVDA | "Attending?" group label announced twice. |
Conclusion
The table element should only be used for describing data tables, rather than using tables for layout purposes. If layout tables absolutely cannot be avoided, then using role="presentation"
is effective at ensuring that redundant table information isn't announced to screen reader users. However, this can't be relied on alone when form controls within fieldset
/legend
elements are in layout tables for JAWS users.
If it's impossible to avoid layout tables for the whole website, at least avoid using tables for layout within fieldset
/legend
elements. If this isn't possible, then the repair technique is to wrap the table in an element with role="group"
and an aria-labelledby
attribute that identifies the group label (the legend
), and the table
element and each table cell must be overridden with role="presentation"
.
The repair technique for ensuring that form controls in a layout table within fieldset
/legend
elements is probably more complicated than not using layout tables in the first place, and duplicates the group label for assistive technologies that correctly ignore layout tables with role="presentation"
. Sometimes, it's difficult to find a repair technique that works in all situations, but using the most semantically appropriate elements will always result in a more accessible experience.
Category: Accessibility.
[layout_tables_repair_techniques.php#comment1]
> [...] to remove the layout table, but sometimes this option is too costly [...]
Not only costy. Also sometimes it's impossible to achieve certain layout (fluid ones with a footer) without tables.
Posted by D on
[layout_tables_repair_techniques.php#comment2]
Just a question: Have you tried to reinforce the role of the input element by using role="radio" ? In theory, that should work.
Posted by Leif Halvard Silli on
[layout_tables_repair_techniques.php#comment3]
Hi D,
For at least 8 years, probably more, there have been numerous articles on how to achieve multi-column layouts with footers using CSS without using layout tables.
Hi Leif,
The role of the input element is not an issue. The role is relayed correctly. The problem is with the group label when the elements in the group are contained in a layout table. The roles of the elements in the table are correct, including the accessible name, but the group label required for context for each element in the group (usually from the legend element) is lost.
Posted by Gez on
[layout_tables_repair_techniques.php#comment4]
Hi Gez,
Right. But it is actually a bit difficult to understand the problem. Is it so that Jaws, without the repair, simply jumps over the legend?
In the summary you seem to hint that the legend sounds *different*, without the repair.(So, it is not simply jumped over.) Quote: "not announced as a label for the group". (Thus it could be announced, but "not as a label".)
However, I have tested your code example in Windows 7 with IE9 and Jaws 13, and I am not able to hear any difference whether I use a table with role=presentation or whether I remove the table completely. Should I try another OS/UA/Jaws combination to experience the error?
Btw, part of my confusion is probably that in VoiceOver, then the legend is announced once for each radio button plus once at the start of the form field. So it is read 4 times. This is not too far from what you achieve with aria-describedby in the first repair technique — but which you considered too much/too wordy.
Posted by Leif Halvard Silli on
[layout_tables_repair_techniques.php#comment5]
@Gez
Yes but I have yet to find one tableless layout that is 100% fluid.
Point me one please.
(And besides, frankly, using floats for columns and semantically useless <div> wrappers is a more terrible hack than <table role=presentation> )
Posted by D on
[layout_tables_repair_techniques.php#comment6]
Thank you for explaining this in such detail. Timely for me - I just accepted a freelance job to completely revamp a website designed in 1999 using tables for their catalog.
Wendy
Posted by Wendy on