I'm trying to ensure that a button inside a Shadow DOM gets tab focus before a regular button in the main DOM. one button have tabindex="0", and the other one is 10 but the regular button outside the shadow is always focused first.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
button{
padding: 1%;
border-radius: 10px;
width: 100px;
height: 50px;
}
button:focus {
background-color: red;
}
</style>
</head>
<body>
<div id="shdow">
</div>
<button tabindex="10">focus me </button>
<script>
// set shadow root with button taindex 0
document.querySelector('#shdow').attachShadow({mode: 'open'}).innerHTML = `
<style>
button{
padding: 1%;
border-radius: 10px;
width: 100px;
height: 50px;
}
button:focus {
background-color: green;
}
</style>
<button tabindex="0">focus me </button>`;
</script>
</body>
</html>
I'm trying to ensure that a button inside a Shadow DOM gets tab focus before a regular button in the main DOM. one button have tabindex="0", and the other one is 10 but the regular button outside the shadow is always focused first.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
button{
padding: 1%;
border-radius: 10px;
width: 100px;
height: 50px;
}
button:focus {
background-color: red;
}
</style>
</head>
<body>
<div id="shdow">
</div>
<button tabindex="10">focus me </button>
<script>
// set shadow root with button taindex 0
document.querySelector('#shdow').attachShadow({mode: 'open'}).innerHTML = `
<style>
button{
padding: 1%;
border-radius: 10px;
width: 100px;
height: 50px;
}
button:focus {
background-color: green;
}
</style>
<button tabindex="0">focus me </button>`;
</script>
</body>
</html>
Share
Improve this question
asked Jan 19 at 12:56
ShobiDobiShobiDobi
2462 silver badges15 bronze badges
3
|
3 Answers
Reset to default 1That's something known. You shouldn't use tabindex with values other than 0 and -1.
When there are naturally focusable elements without tabindex and elements having tabindex=0 mixed with elements having tabindex>0, then all the former ones get the focus after all the later ones, or the converse. The tab order quickly becomes uncontrollable.
If you use somewhere tabindex>0, then all focusable elements must have tabindex>0 to make sure it remains consistent. Hence it's very easy to completely break the tab order and accessibility. And of course it's one more step harder to keep consistency at all times if elements are added or removed, shown and hidden dynamically. That's a nightmare.
See doc about tabindex attribute on MDN for more info: in particular, well read this note: tabindex="0" means that the element should be focusable in sequential keyboard navigation, after any positive tabindex values. but in fact, it can be the opposite, have all tabindex>0 first and then all others.
Shadow DOM Button Integration: The button inside the Shadow DOM is included in a focusSequence array, along with the main document button.
Custom Tabbing Logic:
The keydown event listens for the Tab key. The currentIndex determines which element in focusSequence gets focus next. The default browser tabbing is disabled (event.preventDefault()). Focus Cycling: The focusSequence ensures that the Shadow DOM button is treated as part of the global focus order, giving it priority when cycling through focusable elements.
Testing Steps: Load the page. Press the Tab key. The focus will first land on the Shadow DOM button, then the main DOM button. This approach directly manages focus order and should resolve any issues with Shadow DOM focus priority.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shadow DOM Focus</title>
<style>
button {
padding: 1%;
border-radius: 10px;
width: 100px;
height: 50px;
}
button:focus {
background-color: red;
}
</style>
</head>
<body>
<div id="shadow-container"></div>
<button tabindex="10">Main DOM Button</button>
<script>
// Attach Shadow DOM and add the shadow button
const shadowContainer = document.querySelector('#shadow-container');
const shadowRoot = shadowContainer.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
button {
padding: 1%;
border-radius: 10px;
width: 100px;
height: 50px;
}
button:focus {
background-color: green;
}
</style>
<button tabindex="0">Shadow DOM Button</button>
`;
const shadowButton = shadowRoot.querySelector('button');
const mainButton = document.querySelector('button[tabindex="10"]');
// Custom focus management
let focusSequence = [shadowButton, mainButton];
let currentIndex = 0;
document.addEventListener('keydown', (event) => {
if (event.key === 'Tab') {
// Prevent default tabbing behavior
event.preventDefault();
// Determine the next element to focus
currentIndex = (currentIndex + 1) % focusSequence.length;
focusSequence[currentIndex].focus();
}
});
</script>
</body>
</html>
The solution i found is to set the tab index onto the shadow root element then the next focus will be what comes inside
document.querySelector("button[tabindex='0']").focus()
the<button>
is at theshadowRoot
, correct? – zer00ne Commented Jan 20 at 6:39