This week’s question comes from Luís Osório.
If I have a form, and I want to collect tabular data, I would like to make a table with th for row and column headers and then, in each td I would like to have an input (type=text). The thing is … an input must have a label and each td should reference the th. How should this be done using a table?
Sherpa Jared Smith answers:
Your situation has two distinct accessibility requirements: table cells should be associated to their respective row and/or column headers, and inputs need descriptive labels. We’ll address these individually.
Data Table Accessibility
First, you might ask whether the table really is a data table at all, or whether it is primarily being used to lay out and position the data entry fields. It is most likely that users will navigate from form field to form field, opposed to navigating the table structure. So, adding the table accessibility markup is not likely to have a notable impact.
But it’s easy to add. Simply identify each column header cell with <th scope="col">
(as opposed to td
for table
data cells):
<th scope="col">Name</th>
If you have row headers, use <th scope="row">
. Just make sure that you don’t have empty th
elements, and that empty or data cells always use td
. That’s it for the table.
Form Accessibility
For the form, label the inputs by associating a label
element to an input based on the input
’s id attribute as follows:
<label for="fname">First Name:</label> <input id="fname" type="text">
This works great for most inputs that have a visual label adjacent to them. But for your data entry fields, it’s most likely that one text item (probably your column header) will be used to visually label multiple inputs. Because the id attribute must be unique per page (e.g., you can’t have two elements that each have id="fname"
on one page), this creates a one–to–one relationship between form label and input.
Labelling Multiple Fields
While we can’t label multiple inputs using one label
in a column header, there are several approaches to ensure accessibility. One approach would be to use an off-screen label
element for each input
. Simply add the associated label
adjacent to each control in your HTML, but hide the label
element off-screen using CSS. Sighted users will see the column header, and screen reader users will hear the associated off-screen label
text. This works well, but requires a lot of additional markup.
Alternatively, you could use a descriptive title
attribute value on each input
. Screen readers generally ignore the title
attribute, but will read it for inputs that do not have an associated label
. This will also generate a mouse tooltip on the input
, which may or may not be useful.
Using ARIA
As a final, and perhaps optimal, approach, you could use the aria-labelledby
attribute to overcome the one–to–one label
to input
limitation. The aria-labelledby
attribute goes on the input
itself and references the element(s) on the page (based on id
attribute values) that contains the input’s label
text. The screen reader will read the text within the associated element as the label
text when it encounters the input:
<input aria-labelledby="fname">
For your data entry form, each table
header would have a unique id
value. Then each input
in that header’s row or column would reference that header as an ARIA label. You can even have multiple aria-labelledby
values for each input
— they will be read in the order listed. This is particularly helpful with data entry.
<table>
<tr>
<th scope="col">Name</th>
<th id="agelabel" scope="col">Age</th>
<th id="phonelabel" scope="col">Phone</th>
</tr>
<tr>
<th id="jared" scope="col">Jared</th>
<td><input type="text" size="3" name="age1" aria-labelledby="jared agelabel"></td>
<td><input type="text" size="12" name="phone1" aria-labelledby="jared phonelabel"></td>
</tr>
<tr>
<th id="luis" scope="col">Luís</th>
<td><input type="text" size="3" name="age2" aria-labelledby="luis agelabel"></td>
<td><input type="text" size="12" name="phone2" aria-labelledby="luis phonelabel"></td>
</tr>
<tr>
<th id="aaron" scope="col">Aaron</th>
<td><input type="text" size="3" name="age3" aria-labelledby="aaron agelabel"></td>
<td><input type="text" size="12" name="phone3" aria-labelledby="aaron phonelabel"></td>
</tr>
</table>
Each input in this case would have aria-labelledby
values that reference both the column and row header cells based on their id
attribute values:
<input aria-labelledby="jared agelabel" type="text">
This final approach has great support in all modern browsers and screen readers, and it provides great flexibility when dealing with complex data entry forms.
Discussion
, Jeremey Hustman said:
, Jennifer Little said:
, astrovalley said:
Share Your Thoughts
Please Log in or Sign up to share your thoughts.