Trapping user focus four ways in TypeScript
Tags: a11y, typescript, focus, keyboard
Published: 2023-11-28 12:01:16

---
title: Trapping user focus four ways in TypeScript
published: true
description: Weighing the pros and cons of trapping focus using four different methods
tags: Accessibility, TypeScript, Focus, Keyboard
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u2ejl361p0iggniswvtr.jpg
# published_at: 2023-11-21 16:01 +0000
---
<small>Image by [Ksenia Chernaya](https://www.pexels.com/photo/crop-unrecognizable-female-ophthalmologist-showing-trial-frame-with-lenses-5715885/)</small>

Recently, I had to trap the user's focus within an element. This turns out to be a problem with many solutions. The best answer (spoilers!) turns out to be the simplest.

Note that the Codepens below are in TypeScript and should validate to Airbnb linting rules.

## Capturing keystrokes

My first pass at this problem was based around capturing the <kbd>Shift</kbd> and <kbd>Tab</kbd> keystrokes:

{% codepen https://codepen.io/ross-angus/pen/vYbdwdv %}

### Advantages

* Works if the user presses the keys once, or holds the keys down

### Disadvantages

* Brittle (what if there's a means to move focus which doesn't trigger a keyboard stroke?)
* Registers three different event listeners (`keyup`, `keydown`, `focusout`)

## Remembering the previous element

This updates a variable which represents whatever previously had the focus and works out if the user wants to go forward or backward, based on that.

{% codepen https://codepen.io/ross-angus/pen/WNPpzPp %}

### Advantages

* The solution is quite elegant (I borrowed it from a StackOverflow post)

### Disadvantages

* It only works <q>correctly</q> if the container has elements which can receive focus both before and after it in the tab order

## Using the hidden button method

This uses hidden buttons which flank the content which needs to be contained. When they fall into focus, they push the focus back to the start or end of the element.

{% codepen https://codepen.io/ross-angus/pen/vYbReoP %}

### Advantages

* The container can be the first or last element on the page and it still traps the focus

### Disadvantages

* It feels a little dirty to add `button`s into the DOM - it might have an impact on accessibility.

## Side mission: what happens with a `dialog` element?

HTML5's `dialog` element has modal functionality with native focus trapping. What happens to the focus there?

{% codepen https://codepen.io/ross-angus/pen/yLZMPag %}

Weird. The focus immediately escapes the current tab and ends up in the chrome of the browser. That reminds me of a motivational poster I once saw:

![Motivational-style poster. The image shows a boy running away from the camera on a beach into the sunset. The caption reads The only way to trap focus is to let it escape and return to you when it's ready](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/roltq7ibbk1r1tq70w1g.png)

<small>Image by [Admiral General M. ⭐⭐⭐⭐⭐ GodShepherdly 33277089*](https://www.pexels.com/photo/silhouette-of-boy-running-in-body-of-water-during-sunset-694587/)</small>

Let's rethink the problem: are we trying to punish the user, or are we trying to direct their attention to one UI element? The functional purpose of a modal is that it stops the user from completing any other task on the current page until it is either completed, or dismissed (refreshing the page is dismissing, right?)

If the user needs to make some adjustment to the browser, then this is a task which absolutely *should* be possible, before dismissing the modal. None of the previous solutions have taken this into account.

So, taking inspiration from this:

## Using `tabindex`

What if we just set everything which can receive focus to `tabindex="-1"`, *apart* from elements inside the element we want the user focused on?

{% codepen https://codepen.io/ross-angus/pen/oNmqppz %}

### Advantages

* Allows users to adjust the browser using keyboard navigation
* Relatively simple code
* No event handlers (apart for the button which turns the focus trap off)

### Disadvantages

* Potentially a whole heap of DOM manipulation (do attributes count as DOM manipulation?)