focus-trap

Keyboard users should be able to reach and exit any UI part that contains controls or form elements.

Who might be affected
Screen Reader
Keyboard
Switch Control

Description

Focus trap refers to a situation where keyboard users are stuck in a focus loop within a component or a section of the UI that does not allow them to move forward or backward to other parts of the UI. This usually happens when focus management is not handled correctly using the "nextFocusFowrard" attribute.

Quick Fixes

Android (.xml):

Default Order

To preserve the default focus order, do not set any 'nextFocus' attribute to any view on a screen. As a result, the focus will move in a natural order: from the top left view to the bottom right.

With overrides to the default order

To make the user specified focus order, set to the one or more views one of the next focus attributes, depending on your needs:

(nextFocusLeft, nextFocusRight, nextFocusUp, nextFocusDown, nextFocusForward)

1 → 2 → 3

1<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:layout_width="match_parent"
3 android:layout_height="match_parent"
4 android:orientation="vertical"
5 android:padding="16dp">
6
7 <Button
8 android:id="@+id/button1"
9 android:text="Button 1"
10 android:layout_width="wrap_content"
11 android:layout_height="wrap_content"
12 android:nextFocusDown="@+id/button2"
13 android:layout_marginBottom="8dp" />
14
15 <Button
16 android:id="@+id/button2"
17 android:text="Button 2"
18 android:layout_width="wrap_content"
19 android:layout_height="wrap_content"
20 android:nextFocusDown="@+id/button3"
21 android:layout_marginBottom="8dp" />
22
23 <Button
24 android:id="@+id/button3"
25 android:text="Button 3"
26 android:layout_width="wrap_content"
27 android:layout_height="wrap_content"
28 android:nextFocusDown="@+id/button1" />
29
30</LinearLayout>

Android (Java):

Java Code (you can use one or all, this is just the sample, depending on the direction.

1 // Set next focus directions for button1
2 button1.setNextFocusDownId(R.id.button2);
3 button1.setNextFocusUpId(R.id.button3);
4 button1.setNextFocusLeftId(R.id.button3);
5 button1.setNextFocusRightId(R.id.button2);
6 button1.setNextFocusForwardId(R.id.button2);
7
8 // Set next focus directions for button2
9 button2.setNextFocusDownId(R.id.button3);
10 button2.setNextFocusUpId(R.id.button1);
11 button2.setNextFocusLeftId(R.id.button1);
12 button2.setNextFocusRightId(R.id.button3);
13 button2.setNextFocusForwardId(R.id.button3);
14
15 // Set next focus directions for button3
16 button3.setNextFocusDownId(R.id.button1);
17 button3.setNextFocusUpId(R.id.button2);
18 button3.setNextFocusLeftId(R.id.button2);
19 button3.setNextFocusRightId(R.id.button1);
20 button3.setNextFocusForwardId(R.id.button1);

Android (Kotlin):

1 // Set next focus directions for button1
2 button1.nextFocusDownId = R.id.button2
3 button1.nextFocusUpId = R.id.button3
4 button1.nextFocusLeftId = R.id.button3
5 button1.nextFocusRightId = R.id.button2
6 button1.nextFocusForwardId = R.id.button2
7
8 // Set next focus directions for button2
9 button2.nextFocusDownId = R.id.button3
10 button2.nextFocusUpId = R.id.button1
11 button2.nextFocusLeftId = R.id.button1
12 button2.nextFocusRightId = R.id.button3
13 button2.nextFocusForwardId = R.id.button3
14
15 // Set next focus directions for button3
16 button3.nextFocusDownId = R.id.button1
17 button3.nextFocusUpId = R.id.button2
18 button3.nextFocusLeftId = R.id.button2
19 button3.nextFocusRightId = R.id.button1
20 button3.nextFocusForwardId = R.id.button1

Fail example

This is a particular case of a User Specified Order when the current view creates a loop, either by itself or through a chain of user-specified orders.

1 → 2 → 1

1<Button
2 android:id="@+id/button1"
3 android:text="Button 1"
4 android:layout_width="wrap_content"
5 android:layout_height="wrap_content"
6 android:nextFocusDown="@+id/button2"
7 android:layout_marginBottom="8dp" />
8
9 <Button
10 android:id="@+id/button2"
11 android:text="Button 2"
12 android:layout_width="wrap_content"
13 android:layout_height="wrap_content"
14 android:nextFocusDown="@+id/button1"
15 android:layout_marginBottom="8dp" />
16
17 <Button
18 android:id="@+id/button3"
19 android:text="Button 3"
20 android:layout_width="wrap_content"
21 android:layout_height="wrap_content" />

1 → 3 → 1 The current implementation does not support cases when several views point to the same view. In this scenario, an internal error is thrown, and the focus trap is not displayed.

How Users Are Affected

Keyboard users may be unable to reach and operate all the UI parts.

WCAG Success criteria

This issue might cause elements to fail one or more of the following Success criteria:
2.1.2 No Keyboard Trap (A)