focus-trap demo
In the demos below, you'll be able to tell that a focus trap is active because it will turn pink. You should also be able to tell because it will trap your focus!
When a trap is active, you can deactivate it by pushing its deactivate button or, if the demo allows, pressing the Escape key.
Before you try the demos, if you're using macOS, make sure you have enabled keyboard navigation in order to use the Tab key to move focus between all controls.
In Ventura (v13), you'll find this under System Settings > Keyboard
.
Prior to Ventura, it may have been under System Settings > General
.
FireFox and Safari, for example, respect this setting and without it enabled, you won't get a good experience. Chrome appears to ignore it (at least in v109). While focus-trap will still trap all the right nodes, the browser/OS will not let you access all tabbable nodes on the page, nor in any traps you activate, while using the Tab key.
animated dialog
This focus trap will fade in when activated and fade out when deactivated. Once the trap has been activated
(i.e. onPostActivate
), a "trap activated" message appears next to the Activate button.
Note that the fade animation needs to be written yourself, it is not built into the library.
Trap activated
animated trigger
The activation button for this focus trap will fade out when the trap activates and fade in when the trap deactivates.
If the returnFocus
option is true
, the activation button will receive focus after the trap deactivates.
If the returnFocus
option is false
, the Deactivate button will retain focus after deactivation because that is the last element that was clicked prior to deactivation.
Once the trap has been deactivated (i.e. onPostDeactivate
), a "trap deactivated" message appears next to the "returnFocus" option.
Note that the fade animation needs to be written yourself, it is not built into the library.
Trap deactivated
configurable escape behavior
This focus trap leverages the escapeDeactivates
option as a function
rather than a boolean. By default, pressing ESC will deactivate the trap, but while the trap
is active, you can check or uncheck the option, and the ESC key will behave accordingly.
Since the escapeDeactivates
option received the related KeyboardEvent
,
it could be used to programmatically decide if the ESC key should allow the trap to be
deactivated, perhaps based on the node that's currently focused, or some other condition.
escape key cancelation
This demo shows how the Escape key can be canceled from a child within the focus trap.
initial element, no escape
If initialFocus
is a selector or DOM node, focus will jump to that specified element when the trap activates.
If initialFocus
is false
(or a function that returns false
), no element will be given focus when the trap activates.
initialFocus
is set to the first tabbable element by default.
Also, in this demo the Escape key does not deactivate the focus trap. You must click the button.
tricky initial focus
In this focus trap, the single focusable button is hidden (the ones you see at first have tabindex="-1"
).
If you activate the trap in this state, the fallbackFocus
option is used to focus the container.
If, however, you first make the focusable button visible by clicking "show focusable button", that button will receive focus when you activate the trap.
initial focus selector with fallback
If the initialFocus
option is a selector string that refers to a node that doesn't exist when the trap is activated, the trap will throw an error by default because the option was specified (same as setting the option to a node, or a function that returns a node).
This behavior can be avoided, however, by using the fallbackFocus
option. If that option isn't configured, however, an error will still be thrown. Note that the initialFocus
option can also be set to false
(or a function that returns false
) to avoid initial focus altogether.
In this demo, you can toggle whether the initialFocus
node (the "initial focus" button) exists or not when the trap is activated. If it exists, it'll be initially focused (even though it's not at the start of the regular tab order inside the trap). If it doesn't exist, the trap's container will be focused instead, thanks to the fallbackFocus
option (but only once as the container is only focusable, not tabbable).
initially focused container
When this focus trap activates, initial focus is on the containing element (which has tabindex="-1"
and is therefore not tabbable).
When you tab through the trap, focus does not return to the containing element.
Also, clicking on an outside element automatically deactivates this trap.
hidden treasures
Focusable nodes are initially hidden and then revealed within the trap.
Use Escape to exit.
sibling traps
Sibling focus traps. When the second trap is deactivated, focus returns to the first trap.
input activation
Any change to the input content triggers automatic activation of the focus trap, without changing selection within the input.
delay
focus-trap ensure that the placement of focus within the trap is slightly delayed, so the focused element does not capture the event that originated the activation of the focus trap. For example, the same Enter keystroke won't open and close this trap.
No delay
focus-trap ensure that the placement of focus within the trap is not delayed, so the focused element captures the event that originated the activation of the focus trap.
radio group
A default focus trap that contains a radio group. You should be able to change the radio group with the arrow keys, but Tab should only focus the active radio. (Notice that you need a checked radio in the group for things to work as expected.)
iframe
This focus trap contains an iframe. You should be able to tab into and out of the iframe. But you need to have a focusable element before and after the iframe for things to work as expected.
working inside an iframe
This focus trap can be executed inside an iframe. You should be able to trap the focus inside. You need to have provided the iframe document to the focus-trap before for things to work as expected.
⚠️ As it executes inside the iframe, it cannot prevent clicks on links in the parent document. This is the same drawback as you might experience when putting a focus-trap inside a Shadow DOM.
inside an open shadow dom
This focus trap is instantiated inside an open Shadow DOM. Some users of this library use Web Components to create things like modal dialogs, which means focus needs to be trapped inside the Shadow DOM instance.
When the mouse is clicked on an element inside an open Shadow DOM,
the target node in event.target
is always the shadow host (the node to
which the shadow is attached). Fortunately, event.composedPath()
, a
modern API on the event object, allows focus-trap to determine the actual node that
was clicked inside the shadow.
⚠️ focus-trap is unable to work with closed Shadow DOMs because
event.composedPath()
can't see inside it. In this case, both this
modern API and event.target
point to the shadow host as the actual
element that was clicked, and focus-trap will always determine that the click was
outside the trap (and either prevent it, or always deactivate the trap when the
clickOutsideDeactivates
option is true).
⚠️ See the warning about selector string options when putting a focus trap inside an open Shadow DOM.
with shadow dom
This focus trap contains tabbable elements that are inside
open and closed Shadow DOMs. It configures tabbable
to look for Shadow DOM
elements and provides a reference to the closed Shadow when requested.
allowOutsideClick option
This focus trap can be closed also by clicking a button outside of the trap that is able to control the trap (which is the "activate trap" button in this example). ESC is disabled for this trap.
NOTE: This is different from the clickOutsideDeactivates where merely clicking outside the trap would cause it to be deactivated. In this example, the outside click must take place on the "activate/deactivate trap" button, whose click event handler will programmatically deactivate the trap.
clickOutsideDeactivates option
This focus trap can be closed simply by clicking anywhere outside, and the click outside will also go through and do what it was intentionally dispatched to do (like toggling the checkbox). ESC is disabled for this trap.
The returnFocusOnDeactivate
option controls whether focus should be returned to the last-focused element when the trap was activated (the "activate trap" button in this demo) or not:
- If this option is
true
(the default behavior), even if you click on a focusable node outside the trap (like the checkbox), while the click will go through to toggle the checkbox, it will not be focused once the trap is fully deactivated. Focus will return to the "activate trap" button. - If
false
, then it doesn't matter what you click on (focusable or not), focus will go away from the trap to where you clicked, or to nowhere if you clicked on something not focusable (like the page/document or a heading). So if you click on the checkbox, it will remain focused once the trap is fully deactivated.
setReturnFocus option
With setReturnFocus it is possible to overwrite the returnFocusElement.
setReturnFocus as function
As the function is executed in the process of deactivation, it allows you to dynamically specify which element should be focused after deactivation.
Once the trap is activated, you can still click on one of the 3 deactivate buttons below in order to deactivate the trap.
multiple elements
You can pass multiple elements, and the focus will be kept within those element boundaries (darker color). Clicking outside deactivates.
multiple elements with delete
Pass multiple elements. Update the tabbable nodes in any of those elements on the fly, ensuring there's always at least one container with at least one tabbable node in it at all times.
multiple elements with delete ALL
Pass multiple elements. With the fallbackFocus option configured to the "deactivate trap" button, remove ALL nodes in all trap containers to have the focus fall back to the "deactivate" button on the next tab key press.
Container 1
Container 2
Not in trap (fallback; clicks allowed)
multiple traps with multiple elements
You can have multiple traps with multiple elements. Each trap's elements are in a darker color when their associated trap is active. Clicking outside on the activate and deactivate trap buttons is allowed.
Negative tabindex
This trap has an element with a negative tabindex in the middle, which means that element is focusable but not tabbable. Use the mouse to set focus to it. Once it has focus, you can still use the tab key to go forward or backward in the normal tab order.
Negative tabindex last
This trap has an element with a negative tabindex and it's the last element, which means that element is focusable but not tabbable. Use the mouse to set focus to it. Once it has focus, you can still use the tab key to go forward or backward in the normal tab order.
Positive tabindex
This trap has 3 buttons which have a positive (greater than 0) tabindex attribute. Per tab order rules, they should get focused first (in 1-2-3 order tabbing forward), then all tabbable nodes without a tabindex attribute (the 3 links, "tabindex 0" and "tabindex ?") should get focused in document position order. The same should happen in reverse when shift+tabbing.
❗️ Positive tabindex support is limited to single-container traps. It
is also not 100% accurate to browser behavior in one edge case, which is when tabbing
from a negative tabindex node (focusable, not tabbable). In this case,
browsers will set focus to the next tabbable node in document position order,
regardless of tabindex
value (zero or positive). Focus-trap, however, will
set focus to the next tabbable node in DOM order. Most of the time, the
result will be the same, but there may be slight differences.
⚠️ Using positive tab indexes is not recommended, primarily for accessibility reasons.
⚠️ Tabbing from the last positive tabindex node leading to a zero/default tabindex node
will cause a momentary focus escape which focus-trap will identify and rectify
to keep tab order working as expected. This momentary escape, however, means some node outside
the trap will get focused before focus-trap is able to pull focus back into the trap. If that
node is out of view, this may cause the window to scroll to it, not to mention the
focus
event this outside node will unavoidably receive.
With open web component
This trap contains an element which is defined in an open web component (i.e. a web component with an open Shadow DOM).
Global trap stack
Use window to have access to trap stack. This allows to have more than one focus-trap library version in the same page.
Customized keyboard navigation
This trap uses the isKeyForward()
and isKeyBackward()
options to enable the use of the j
(backward) and k
(forward)
keys to navigate the trapped elements using the keyboard.
💬 The tab
key still works, but not completely, as it's no longer managed by
the trap. To make full use of these options, the tab
key should be
suppressed entirely or handled differently. You might use these options in a dropdown
list where you're moving focus between items using the arrow keys while reserving the
tab
key for moving beyond the dropdown itself.
works with inert elements
This trap contains an inert button. Browsers that support this new HTML attribute will render it completely non-interactive, and focus-trap will ignore it too.
⚠️ Check the
browser compatibility
for the browser you're using right now before trying this demo. It will not work properly
if your browser doesn't support the inert
attribute.
Keeps focus when removing DOM elements
If the currently focused elemented is removed from the DOM, the
focus can move to the document body
.
focus-trap ensures that focus remains within the trap.