Summary
There are several techniques for creating tables with static header rows, but very few of the examples I have come across are accessible or do not work cross-browser. This post looks at some of the current solutions, and outlines a method using WAI-ARIA to fix the accessibility issues of the more popular techniques.
Author: Gez Lemon
Current techniques
Ideally, this problem should be solved using just CSS by specifying the height
, overflow
, and display
properties on the body of the table (tbody
). This works to an extent, but is difficult to get working cross-browser. Pure CSS Scrollable Table with Fixed Header is an old but good example of a CSS version that works in most browsers and screen readers, but doesn't render properly in IE9.
Most solutions use JavaScript, and typically duplicate the header of the table into a separate table. jQuery Plugin for Fixed Header Tables is a good example of this technique, but there are plenty of other examples that essentially do the same. This technique is popular, because it's easier to get to work cross-browser. The accessibility of this solution is not so good, as it results in a separate table just for the headers. Most of the solutions that attempt to remain accessible duplicate the headers in the scrolling table and hide them visually so they are still available to screen readers; but there is still a redundant table in the content containing just the headers.
WAI-ARIA as a repair technique
As having two tables seems to be the most reliable in terms of portability, one solution would be to repair the technique so that it appears as a single table to assistive technologies. Consider a data table that determines calories burned depending on the weight and pace of a runner with the headers in a completely different table so that the main body can be set to scroll, as most approaches appear to use this or a variation on this technique.
The process to repair and merge the table with WAI-ARIA is outlined below:
- Merge the two tables (headings and main data) in a single container that has a role of grid.
- Change the role of the two existing data tables to presentation so they are ignored by assistive technologies.
- Remove hidden headers from the main data table if they have been provided.
- Use the roles row, columnheader, rowheader, and gridcell to create the structure of a single table.
- Use aria-labelledby to create an association between the table and the table's header, so that the table is announced with the context of a heading, similar to a native HTML data table having a
caption
. - Unless stated otherwise, a grid is considered editable. The aria-readonly attribute should be used to indicate that it's a read-only data table.
The following is the resulting structure:
Alternatively, the structure could be defined with WAI-ARIA removing the HTML tables completely, as it's far easier to control the div
elements with CSS than the tables.
Accessibility of this solution
This solution works well using JAWS. Unfortunately, it does not work well with VoiceOver on an iPad iOS6 or on a Mac. VoiceOver recognises the table as a single table, but does not map the columnheader
or rowheader
roles as column and row headers properly. In iOS6 they're ignored, and on a Mac they're reported incorrectly (thanks to Steve Faulkner for testing these roles on a Mac with VoiceOver).
Further, the W3C validator currently reports an error for columnheader
and rowheader
roles contained in a row
, but a bug has been filed and it is expected that this issue will be resolved shortly.
Conclusion
The best solution would be to use CSS to scroll the main body while leaving the headers static, but there doesn't appear to be an accessible solution that works cross-browser. If anyone knows of a solution, please post in the comments. As the most popular technique to have static headers in a data table is to put the headers and the table content in two different tables, then this repair technique seems reasonable (or defining the structure with WAI-ARIA alone). It's a shame the WAI-ARIA columnheader
and rowheader
roles are not better supported in VoiceOver, but support is likely to improve.
Category: Accessibility.
[accessible_data_tables_static_headers.php#comment1]
The problem with CSS-only solutions is that they're not accessible to voice dictation users. The scrollbar that is created does not respond to voice commands.
We played with these a bit and came up with this idea: http://examples.simplyaccessible.com/scrollable-table/slider.html
The challenge remains that voice dictation users wouldn't know what voice commands to use to call it, which is still something we're looking into. At this point, however, at least once the item has focus a voice dictation user can manipulate the slider.
Posted by Karl Groves on
[accessible_data_tables_static_headers.php#comment2]
Hi Karl,
Thank you for your insightful comments and great point about voice commands with pure CSS solutions. The example doesn't work properly for me using the keyboard in IE8 (the thumb moves up and down, but the table remains static), and it doesn't render properly in IE9, but I understand it's a work in progress. It works well with JAWS, Firefox and Chrome.
Posted by Gez on
[accessible_data_tables_static_headers.php#comment3]
This CSS scrolling table is also not accessible without a mouse it seems:
http://www.pmob.co.uk/temp/table-fixed-header-example.htm
but it was a neat idea.
Posted by Stomme poes on
[accessible_data_tables_static_headers.php#comment4]
I wonder if there was any CSS associated with the example tables when testing with Safari. Table recognition is problematic if there is no border. Voiceover requires an explicit border.
@Karl We're working with Nuance to get better support for accessibility APIs in browsers. That should make voice recognition interaction easier.
Posted by Pratik Patel on
[accessible_data_tables_static_headers.php#comment5]
Hi Pratik,
That's very interesting. I didn't realise that VoiceOver required an explicit border, but the examples I tested did have a border.
Posted by Gez on
[accessible_data_tables_static_headers.php#comment6]
Hi Jez,
In that case, border is not the issue. I wanted to make certain that test results weren't adversely affected.
Posted by Pratik Patel on
[accessible_data_tables_static_headers.php#comment7]
@Pratik
"We're working with Nuance to get better support for accessibility APIs in browsers. That should make voice recognition interaction easier."
That's great to know, as the lack of support for standards in such products causes much grief.
Posted by steve faulkner on
[accessible_data_tables_static_headers.php#comment8]
I've used the following before to solve this problem without using role=grid.
The "Visible" table header which is intended to remain static is hidden from AT using aria-hidden="true" and role="presentation" (really just a fallback in case hidden isn't supported)
The Screen reader table header row is hidden off-screen
The Links (intended to enable sorting - not functional in this example) are made non-focusable in the "Visible" header using tabindex=-1
The focusable links are off-screen so we use JavaScript to simulate a focus on the "visible" header
onclick events to do the sorting are needed on both headers (not supplied in the linked example)
http://jnurthen.users.sonic.net/hiddenTableHeader.html
Some imagination is required visually as I have made no attempt to size the rows in the Static table with the cells in the actual data table.
Posted by James Nurthen on
[accessible_data_tables_static_headers.php#comment9]
Thanks, James. This information is very useful. I've tested it and it works well.
If aria-hidden="true" fails, role="presentation" wouldn't be useful, as there would still be a redundant information in the content (the table semantics would be removed, but the content would still be available).
I appreciate you sharing this, as this is a very good solution. Thanks.
Posted by Gez on
[accessible_data_tables_static_headers.php#comment10]
@Gez I put the role=presentation there thinking that some erroneous text is better than an erroneous table. Thinking about it more a 1 by X table gets treated as presentational by most AT anyway so role=presentation is completely unnecessary.
Posted by James Nurthen on
[accessible_data_tables_static_headers.php#comment11]
Why is is mandatory that i leave a comment? That seems kinda pushy to me.
Posted by bert woodall on
[accessible_data_tables_static_headers.php#comment12]
Ha - that has to qualify for the best off-topic comment
Posted by Gez on
[accessible_data_tables_static_headers.php#comment13]
The table can be transformed to 2 columns only, repeating headers, using only CSS, custom attributes and before/content pointing to said attribute. Still quite annoying HTML, not friendly to WYSIWYG, and not 100% accurate with widths. I'd prefer to analyse each individual project individually because the data displayed on the desktop might not be interesting to show on a mobile, ie the mobile to use a cut down version only.
Posted by Steve on