I am running into an issue with focus trapping, I have a popup with a command bar that has 3 span elements that use icons, and when I tab through them it works fine, up until I tab over to the last element and as soon I land on it, it brings me right back to the first element, and what I was expecting to happen is when I tab away from the last element then it should go back to the first element.. Click on the Save Icon and use the tab key
const element = document.getElementById("PromptsDialog");
const focusableElements = element.querySelectorAll("span:not([disabled])");
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
element.addEventListener("keyup", function(e) {
if (e.key === "Tab") {
if (document.activeElement === lastFocusableElement) {
firstFocusableElement.focus();
e.preventDefault();
}
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="PromptsDialog" style="display: block;">
<div >
<h4 style="margin-top:-4px;">Options Prompt</h4>
<div id="PromptsCommand" >
<div style="height:inherit">
<span type="" tabindex="1" data-toggle="tooltip" data-placement="top" title="" data-original-title="Save" id="btnSaveWindow"><i ></i></span>
<span type="" tabindex="2" data-toggle="tooltip" data-placement="top" title="" data-original-title="Remove Item" id="btnRemoveFromItemsGrid"><i ></i></span>
<span type="" tabindex="3" data-toggle="tooltip" data-placement="top" title="" data-original-title="Close" id="btnClosePromptDialog"><i ></i></span>
</div>
</div>
</div>
</div>
Any idea on why it happens when I land on the last element?
Thanks!
CodePudding user response:
It immediately goes back to the first element more or less because of the timing of events. In your code you are using the 'keyup' event, which triggers as soon as the 'Tab' key is released. However, the last element is focused when the 'Tab' key is pressed (down), and so pressing tab will first focus the last element, then execute your code when tab is released, immediately moving back to the first element.
An easy fix is to use the 'keydown' event instead.
const element = document.getElementById("PromptsDialog");
const focusableElements = element.querySelectorAll("span:not([disabled])");
const firstFocusableElement = focusableElements[0];
const lastFocusableElement = focusableElements[focusableElements.length - 1];
element.addEventListener("keydown", function(e) {
if (e.key === "Tab") {
if (document.activeElement === lastFocusableElement) {
firstFocusableElement.focus();
e.preventDefault();
}
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.2/css/all.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="PromptsDialog" style="display: block;">
<div >
<h4 style="margin-top:-4px;">Options Prompt</h4>
<div id="PromptsCommand" >
<div style="height:inherit">
<span type="" tabindex="1" data-toggle="tooltip" data-placement="top" title="" data-original-title="Save" id="btnSaveWindow"><i ></i></span>
<span type="" tabindex="2" data-toggle="tooltip" data-placement="top" title="" data-original-title="Remove Item" id="btnRemoveFromItemsGrid"><i ></i></span>
<span type="" tabindex="3" data-toggle="tooltip" data-placement="top" title="" data-original-title="Close" id="btnClosePromptDialog"><i ></i></span>
</div>
</div>
</div>
</div>