In the following code you can see a simplified working example of a table where each row has an options button which triggers a menu with more buttons. However, when you try to tab through the focusable elements you will see that despite opening the menu, you will still have to tab through all the rows before you get into the menu.
My desired result is that you if you for example are focusing the "options" button in row 1, and then proceed to open it, the next tab press should bring you into the first button of the menu. Then as you tab through all the buttons of the menu, you should then be brought back to the "options" button in row 2.
How do I achieve this?
(Also, I cannot remove the overflow: hidden
from the table which is why I am using an absolutely positioned menu outside of the table element in the first place)
const menu = document.getElementById("menu");
const table = document.getElementById("table");
const option_buttons = Array.from(table.querySelectorAll("button"));
table.addEventListener("click", (e) => {
if (option_buttons.includes(e.target)) {
e.stopPropagation();
menu.style.top = e.target.getBoundingClientRect().top + "px";
menu.style.display = "flex";
document.documentElement.addEventListener("click", (e) => {
if (e.target !== menu && !menu.contains(e.target)) {
menu.style.display = "none";
}
});
}
});
.table {
overflow: hidden;
}
.row {
display: flex;
gap: 2rem;
padding: 0.5rem 0;
border-bottom: 0.0625rem solid #ccc;
}
.menu {
display: none;
position: absolute;
left: 11rem;
padding: 0.5rem;
background: #ccc;
flex-direction: column;
gap: 0.5rem;
}
.menu::before {
content: '';
position: absolute;
top: 0;
left: -0.75rem;
width: 0;
height: 0;
border-top: 0.75rem solid transparent;
border-bottom: 0.75rem solid transparent;
border-right: 0.75rem solid #ccc;
}
button:focus {
background: skyblue;
}
<div class="table" id="table">
<div class="row">
<span>Item 1</span>
<button class="btn_option" type="button">Options</button>
</div>
<div class="row">
<span>Item 2</span>
<button class="btn_option" type="button">Options</button>
</div>
<div class="row">
<span>Item 3</span>
<button class="btn_option" type="button">Options</button>
</div>
<div class="row">
<span>Item 4</span>
<button class="btn_option" type="button">Options</button>
</div>
<div class="row">
<span>Item 5</span>
<button class="btn_option" type="button">Options</button>
</div>
</div>
<div class="menu" id="menu">
<button type="button">View</button>
<button type="button">Edit</button>
<button type="button">Delete</button>
</div>
2
Answers
NOTE: This solution is the closest I have gotten to achieve everything I have outlined instead of just a simple focus() into the menu. It is NOT complete yet but seeing as it is my own post, I hope its okay that I share what I have so far here can help others if they wish to try and solve this.
I am using invisible buttons as "focus managers" which when focused triggers JS which will determine where the focus should go next. Apart from the fact that I have yet to find a way to get the next naturally focusable element, I think this achieve everything I outlined.
The only concerns I have is that using invisible buttons might not be SEO or Accessibility friendly (would appriciate if someone could chime in on that).
I made some code changes like as below. So, when the "Options" button is clicked, it sets the focus on the first button inside the menu. This way, when you press the Tab key, the focus will move through the menu buttons, and when you reach the last menu button, the next Tab press will bring you back to the "Options" button in the next row.