Preword
Apologies to OP there are a few things in this answer that may be misleading if the row is meant to take people to another page.
Firstly for options 1 and 2 below swap <button>
s for <a>
. There is no need for an event listener or onclick
as anchors do everything that is needed.
For option 3 I would add a little extra bit to the text that explains this takes you to a new page.
If you opt for using the column text as the accessible text (i.e. don't override with aria-label
) then you should add aria-describedby
and add a paragraph that says ", click to open in new page".
<p id="newPageText" class="visually-hidden">click to open (new page)</p>
<tr class="odd" tabindex="0" aria-describedby="newPageText">
<td class="">
<div class="input-sm" status_id="undefined">Blah Blah</div>
</td>
<td class="sorting_1">
<div class="input-sm" status_id="undefined">Blah blah</div>
</td>
<td class="">
<div class="input-sm" status_id="undefined">blah blah</div>
</td>
</tr>
This would then read "Blah Blah, Blah Blah, Blah Blah, click to open in new page" for screen readers. This then fills in the information that a screen reader would read out with semantics (that we lose) by not being able to use an anchor
Short Answer
The reason your aren't hearing any announcement is that your rows are not focusable.
An item must be focusable in order for there to be a click announcement.
Adding tabindex="0"
will fix this issue for keyboard users, for mouse users you should add cursor: pointer
and .tr:hover{}
rules to your CSS.
However there are better options as detailed below.
Long Answer
Having a whole row clickable is not the best idea.
However it would still be preferable to some other options that I detail in "what NOT to do" section so I have included it as the third option below.
Here are a few options from best to worst in terms of expected behaviour and accessibility vs time to implement (the second option is the optimal one but may take a lot of effort to implement).
1. GOOD - Add a button to a column instead - easy to implement
Add a column to the start (or end if more appropriate) of the row and add <button>
into that column to perform the action.
Remove your click handler on the row and attach it to the <button>
(also change it to an event listener in your JavaScript rather than an inline onclick
to make maintainability easier).
Within this button we add some visually hidden text for screen reader users / text only browser users to make the row association clear.
I recommend Visually hidden text as it is still far more robust than aria-label
sadly (although things are getting better!) and has the added benefit of working all the way back to ie6 (although if your site works in IE6 you are better than me! :-) )!
You can build the string within the visually hidden <span>
on the server or build it via JavaScript.
It does not need to contain all of the row information if there is a particular column that is unique / descriptive enough to know what you are editing. I have only added all 3 columns' text to the example as I do not know your data.
Example 1
.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>
<tbody>
<tr class="odd">
<td>
<button>
Perform Action
<span class="visually-hidden">
Column 1, Column 2, Column 3
</span>
</button>
</td>
<td class="">
<div class="input-sm" status_id="undefined">Column 1</div>
</td>
<td class="sorting_1">
<div class="input-sm" status_id="undefined">Column 2</div>
</td>
<td class="">
<div class="input-sm" status_id="undefined">Column 3</div>
</td>
</tr>
</tbody>
</table>
This is the most robust way across all devices and easy to implement which is why it is the top recommendation, however it adds a lot of tab stops and is not the best option if you have time to implement the grid pattern
2. BEST - Recommended Way (if you can't add a column) - hard to implement
Use role="grid"
and follow the grid pattern.
role="grid"
has good screen reader support and is the most semantically appropriate role.
There is a lot more required here as you need to enable keyboard controls such as the use of arrow keys etc.
You would then add a visually hidden paragraph on the page that says "click to edit" and give this an id
(e.g. id="editText"
).
Then on each <td>
you would add aria-describedby="editText"
so that the <td>
would read it's contents followed by "click to edit".
At this point you just add an event listener in your JavaScript to listen for clicks (rather than having inline onclick
attributes.) This answer should give you an idea of how to add an event listener to each <td>
if you are unsure.
W3C example
no example from me as this way requires more work than a Stack Overflow answer justifies! hehe. However it is a good pattern to reuse across the site if you can make it a reusable component. It has a significant advantage that it only introduces one tab stop.
3. AVERAGE - Possible way if you can't use role="grid"
or add a column.
Add tabindex="0"
to the <tr>
.
This will make the row focusable and then announce "clickable" when the user focuses the row via keyboard.
For mouse users who use a screen reader there will still be no click announcement but that is what cursor: pointer
and .tr:hover{}
are for (change the cursor and also ensure visually it is clear this is a clickable region when hovering with the mouse with some visible indicator).
All the row columns will get read out one after the other as the text when it is focused, this may not be appropriate so you might want to add an aria-label
as an override in browsers that support it.
Make sure you style your tr:focus
indicator to be visible and clear if doing this.
tr:hover{
cursor: pointer;
background-color: #cccccc;
}
tr:focus{
border: none;
outline: 2px solid #333;
outline-offset: 2px;
}
<table
class="list lista table table-bordered table-hover dlaList component-editable dataTable"
data-editable-id="dla28475"
style="width: 900px"
id="DataTables_Table_1"
>
<tbody>
<tr class="odd" tabindex="0">
<td class="">
<div class="input-sm" status_id="undefined">Blah Blah</div>
</td>
<td class="sorting_1">
<div class="input-sm" status_id="undefined">Blah blah</div>
</td>
<td class="">
<div class="input-sm" status_id="undefined">blah blah</div>
</td>
</tr>
</tbody>
</table>
The above is the simplest fix but not ideal from an accessiblity perspective due to introducing a lot of tab stops. It will still be usable though on a table with only a few rows.
What NOT to do
DO NOT wrap a row in an anchor <a>
This is not semantically correct and invalid HTML anyway. It would cause issues with some screen readers.
DO NOT use role="alert"
on a <tbody>
.
If you are attempting to alert screen readers to changes in a table ask another question detailing what changes you are trying to announce and I will help you with that.
At the moment it will start reading the table on page load and with every change. It will also read the whole table again even if you just add a row etc. and will probably read the whole table every time you apply a filter. As you can imagine for a screen reader user this would become a nightmare!
DO NOT add <buttons>
to each <td>
or make each <td>
(cell) clickable with tabindex="0"
This would be a nightmare for keyboard users who rely on the Tab key to navigate (i.e. non screen reader users).
You add so many unnecessary tab stops the page may become unusable.
A few other observations / suggestions
- If the table is very long and you end up using option 1 or 3 (adding a tab stop for each row) make sure there is a
skip link
before the table.
- Remove
role="alert"
and aria-live="polite"
as mentioned earlier.
- with your sortable column headers you would be better adding a
<button>
within the <th>
that triggers the sort function, it comes with all your focus states built in etc.
- Remove
role="grid"
from the <div>
- this should sit on the <table>
instead if you use that pattern (option 2 I gave).
- Use event handlers instead of
onclick
attribute - just a general tip for performance and maintainability - nothing to do with accessibility!