|
| 1 | +--- |
| 2 | +outline: "deep" |
| 3 | +--- |
| 4 | + |
| 5 | +<script setup> |
| 6 | +import { registerAll } from '@tapsioss/web-components'; |
| 7 | +registerAll(); |
| 8 | +</script> |
| 9 | + |
| 10 | +# Focus Control |
| 11 | + |
| 12 | +Ensure that your web application can be fully operated with the keyboard only: |
| 13 | + |
| 14 | +- [WebAIM talks about keyboard accessibility](https://webaim.org/techniques/keyboard/) |
| 15 | + |
| 16 | +## Keyboard focus and focus outline |
| 17 | + |
| 18 | +Keyboard focus refers to the current element in the DOM that is selected to accept input from the keyboard. We see it everywhere as a focus outline similar to that shown in the following image: |
| 19 | + |
| 20 | +[](https://legacy.reactjs.org/static/dec0e6bcc1f882baf76ebc860d4f04e5/4fcfe/keyboard-focus.png) |
| 21 | + |
| 22 | +Only ever use CSS that removes this outline, for example by setting `outline: 0`, if you are replacing it with another focus outline implementation. |
| 23 | + |
| 24 | +<section aria-hidden="true" style="background: white; border-radius: 8px; padding: 12px; display: flex; align-items: center; justify-content: center; flex-wrap: wrap; gap: 8px;"> |
| 25 | + |
| 26 | +<tapsi-button variant="brand">عنوان دکمه</tapsi-button> |
| 27 | +<tapsi-rate-slider label="rate-slider"></tapsi-rate-slider> |
| 28 | +<tapsi-radio label="radio"></tapsi-radio> |
| 29 | +<tapsi-switch label="switch"></tapsi-switch> |
| 30 | +<tapsi-checkbox label="checkbox"></tapsi-checkbox> |
| 31 | + |
| 32 | +</section> |
| 33 | + |
| 34 | +## Mechanisms to skip to desired content |
| 35 | + |
| 36 | +Provide a mechanism to allow users to skip past navigation sections in your application as this assists and speeds up keyboard navigation. |
| 37 | + |
| 38 | +### Skip Links |
| 39 | + |
| 40 | +On most pages, keyboard and screen reader users must navigate a long list of navigation links and other elements before ever arriving at the main content. This can be particularly difficult for users with some forms of motor disabilities. Consider users with no or limited arm movement who navigate a web page by tapping their heads on a switch or that use a stick in their mouth to press keyboard keys. Requiring users to perform any action numerous times before reaching the main content poses an accessibility barrier. |
| 41 | + |
| 42 | +Of course, sighted people who use their mouse do not have any trouble with web pages like this. They can almost immediately scan over the page and identify where the main content is. Skip navigation links are useful to give screen reader and keyboard users the same capability of navigating directly to the main content. |
| 43 | + |
| 44 | +The idea is simple enough: provide a link at the top of the page that, when activated, jumps the user to the beginning of the main content area. |
| 45 | + |
| 46 | +```html |
| 47 | +<body> |
| 48 | + <a href="#main">Skip to main content</a> |
| 49 | + <nav role="navigation"> |
| 50 | + <ul> |
| 51 | + <li><a href="/">Home</a></li> |
| 52 | + <li><a href="/about">About</a></li> |
| 53 | + <li><a href="/blog">Blog</a></li> |
| 54 | + </ul> |
| 55 | + </nav> |
| 56 | + <main id="main"> |
| 57 | + <!-- page specific content --> |
| 58 | + </main> |
| 59 | +</body> |
| 60 | +``` |
| 61 | + |
| 62 | +### Landmark Elements |
| 63 | + |
| 64 | +Check [Semantic HTML: Landmark Elements](/semantic-html#landmark-elements) |
| 65 | + |
| 66 | +## Programmatically managing focus |
| 67 | + |
| 68 | +Our React applications continuously modify the HTML DOM during runtime, sometimes leading to keyboard focus being lost or set to an unexpected element. In order to repair this, we need to programmatically nudge the keyboard focus in the right direction. For example, by resetting keyboard focus to a button that opened a modal window after that modal window is closed. |
| 69 | + |
| 70 | +MDN Web Docs takes a look at this and describes how we can build [keyboard-navigable JavaScript widgets](https://developer.mozilla.org/en-US/docs/Web/Accessibility/Keyboard-navigable_JavaScript_widgets). |
| 71 | + |
| 72 | +To set focus in React, we can use [Refs to DOM elements](https://legacy.reactjs.org/docs/refs-and-the-dom.html). |
| 73 | + |
| 74 | +Using this, we first create a ref to an element in the JSX of a component class: |
| 75 | + |
| 76 | +```tsx |
| 77 | +class CustomTextInput extends React.Component { |
| 78 | + constructor(props) { |
| 79 | + super(props); |
| 80 | + // Create a ref to store the textInput DOM element this.textInput = React.createRef(); } |
| 81 | + render() { |
| 82 | + // Use the `ref` callback to store a reference to the text input DOM // element in an instance field (for example, this.textInput). return ( |
| 83 | + <input |
| 84 | + type="text" |
| 85 | + ref={this.textInput} /> |
| 86 | + ); |
| 87 | + } |
| 88 | +} |
| 89 | +``` |
| 90 | +
|
| 91 | +Then we can focus it elsewhere in our component when needed: |
| 92 | +
|
| 93 | +```tsx |
| 94 | +focus() { |
| 95 | + // Explicitly focus the text input using the raw DOM API |
| 96 | + // Note: we're accessing "current" to get the DOM node |
| 97 | + this.textInput.current.focus(); |
| 98 | +} |
| 99 | +``` |
| 100 | +
|
| 101 | +Sometimes a parent component needs to set focus to an element in a child component. We can do this by [exposing DOM refs to parent components](https://legacy.reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components) through a special prop on the child component that forwards the parent’s ref to the child’s DOM node. |
| 102 | +
|
| 103 | +```tsx |
| 104 | +function CustomTextInput(props) { |
| 105 | + return ( |
| 106 | + <div> |
| 107 | + <input ref={props.inputRef} />{" "} |
| 108 | + </div> |
| 109 | + ); |
| 110 | +} |
| 111 | + |
| 112 | +class Parent extends React.Component { |
| 113 | + constructor(props) { |
| 114 | + super(props); |
| 115 | + this.inputElement = React.createRef(); |
| 116 | + } |
| 117 | + render() { |
| 118 | + return <CustomTextInput inputRef={this.inputElement} />; |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +// Now you can set focus when required. |
| 123 | +this.inputElement.current.focus(); |
| 124 | +``` |
| 125 | +
|
| 126 | +When using a [HOC](https://legacy.reactjs.org/docs/higher-order-components.html) to extend components, it is recommended to [forward the ref](https://legacy.reactjs.org/docs/forwarding-refs.html) to the wrapped component using the `forwardRef` function of React. If a third party HOC does not implement ref forwarding, the above pattern can still be used as a fallback. |
| 127 | +
|
| 128 | +A great focus management example is the [react-aria-modal](https://github.com/davidtheclark/react-aria-modal). This is a relatively rare example of a fully accessible modal window. Not only does it set initial focus on the cancel button (preventing the keyboard user from accidentally activating the success action) and trap keyboard focus inside the modal, it also resets focus back to the element that initially triggered the modal. |
| 129 | +
|
| 130 | +::: warning |
| 131 | +While this is a very important accessibility feature, it is also a technique that should be used judiciously. Use it to repair the keyboard focus flow when it is disturbed, not to try and anticipate how users want to use applications. |
| 132 | +::: |
0 commit comments