Skip to content

Instantly share code, notes, and snippets.

@JulienPradet
Last active April 26, 2024 15:40
Show Gist options
  • Save JulienPradet/20dbb7ca06cbd9e2ec499bb2206aab55 to your computer and use it in GitHub Desktop.
Save JulienPradet/20dbb7ca06cbd9e2ec499bb2206aab55 to your computer and use it in GitHub Desktop.
How to use `use` directive to trap focus in Svelte
<script>
import {trapFocus} from "./trapFocus";
</script>
<div use:trapFocus>
<button>Hi!</button>
<button>Second button</button>
</div>
let trapFocusList: HTMLElement[] = [];
if (typeof window !== "undefined") {
const isNext = (event: KeyboardEvent) =>
event.keyCode === 9 && !event.shiftKey;
const isPrevious = (event: KeyboardEvent) =>
event.keyCode === 9 && event.shiftKey;
const trapFocusListener = (event: KeyboardEvent) => {
if (event.target === window) {
return;
}
const eventTarget = (event.target as unknown) as Element;
const parentNode = trapFocusList.find((node) => node.contains(eventTarget));
if (!parentNode) {
return;
}
// List inspired by https://github.com/nico3333fr/van11y-accessible-modal-tooltip-aria/blob/master/src/van11y-accessible-modal-tooltip-aria.es6.js#L47C17-L47C17
const focusable: NodeListOf<HTMLElement> = parentNode.querySelectorAll(
"a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, *[tabindex], *[contenteditable]"
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
if (isNext(event) && event.target === last) {
event.preventDefault();
first.focus();
} else if (isPrevious(event) && event.target === first) {
event.preventDefault();
last.focus();
}
};
document.addEventListener("keydown", trapFocusListener);
}
export const trapFocus = (node: HTMLElement) => {
trapFocusList.push(node);
return {
destroy() {
trapFocusList = trapFocusList.filter((element) => element !== node);
},
};
};
@sandrosxila
Copy link

It's better to use the Node type at the line 13:

const eventTarget = event.target as Node;

Instead of event.keyCode === 9, I would write: event.key === 'Tab'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment