Turns out that making a table row clickable while keeping it accessible, and allowing there to be clickable elements inside it, is a little tricky.
Here's the code I ended up with.
<style>
tbody tr {
cursor: pointer;
}
tbody tr:hover {
background: lightgray;
}
</style>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
</tr>
</thead>
<tbody>
<tr role="button" tabindex="0" aria-label="Show row details">
<td>Data 1</td>
<td><button class="skip">Button</button></td>
<td><a href="#" class="skip"><strong>Link</strong></a></td>
</tr>
</tbody>
</table>
<script>
function handleEvent(e) {
const elements = e.composedPath();
for (const elm of elements) {
if (elm.tagName === "TR") {
break;
} else if (elm.classList.contains("skip")) {
return;
}
}
const space = 32;
const enter = 13;
if (e.type === "click" || e.keyCode === space || e.keyCode === enter) {
e.preventDefault();
console.log("Do something for table row event.");
}
}
document.querySelectorAll("tbody tr").forEach((elm) => {
elm.addEventListener("click", handleEvent);
elm.addEventListener("keydown", handleEvent);
});
</script>
The code is accessible because you can get to the table rows using your keyboard and select them by pressing Space or Enter.
It's possible to add links, buttons etc and have their events handled normally by adding a "skip" class to them (or on a parent element).