tabindex로 포커스 제어
<button>
또는 <input>
과 같은 표준 HTML 요소에는 무료로 내장된 키보드 접근성이 있습니다. 그러나 맞춤형 상호작용 구성 요소를 구축하는 경우 tabindex
속성을 사용하여 키보드에 액세스할 수 있는지 확인합니다.
컨트롤이 키보드에 액세스할 수 있는지 확인 #
Lighthouse와 같은 도구는 특정 접근성 문제를 감지하는 데 탁월하지만 일부 항목은 사람만 테스트할 수 있습니다.
Tab
키를 눌러 사이트를 탐색해 보세요. 페이지의 모든 상호작용 컨트롤에 접근할 수 있나요? 그렇지 않은 경우 tabindex
를 사용하여 해당 컨트롤의 포커스 가능성을 개선해야 할 수 있습니다.
탭 순서에 요소 삽입 #
tabindex="0"
을 사용하여 자연스러운 탭 순서로 요소를 삽입하세요. 예를 들면 다음과 같습니다.
<div tabindex="0">Focus me with the TAB key</div>
요소에 초점을 맞추려면 Tab
키를 누르거나 요소의 focus()
메서드를 호출합니다.
탭 순서에서 요소 제거 #
tabindex="-1"
을 사용하여 요소를 제거합니다. 예를 들면 다음과 같습니다.
<button tabindex="-1">Can't reach me with the TAB key!</button>
이렇게 하면 자연스러운 탭 순서에서 요소가 제거되지만 focus()
메서드를 호출하여 요소에 계속 초점을 맞출 수 있습니다.
tabindex="-1"
을 요소에 적용해도 하위 요소에는 영향을 미치지 않습니다. 자연스럽게 탭 순서에 있거나 tabindex
값으로 인해 탭 순서대로 유지됩니다. 탭 순서에서 요소와 모든 하위 요소를 제거하려면 WICG의 inert
polyfill 사용을 고려하세요. polyfill은 제안된 inert
속성의 동작을 모방하여 요소가 보조 기술에 의해 선택되거나 읽히는 것을 방지합니다.
tabindex > 0
방지 #
0보다 큰 모든 tabindex
는 요소를 자연스러운 탭 순서의 맨 앞으로 옮깁니다. tabindex
가 0보다 큰 요소가 여러 개 있는 경우 탭 순서는 0보다 큰 가장 낮은 값부터 시작하여 위로 올라갑니다.
0보다 큰 tabindex
를 사용하면 화면 판독기가 탭 순서가 아니라 DOM 순서로 페이지를 탐색하기 때문에 안티 패턴으로 간주됩니다. 한 요소의 탭 순서가 더 빨라야 하는 경우 DOM의 이전 위치로 이동해야 합니다.
tabindex
> 0인 요소를 쉽게 식별할 수 있습니다. 접근성 감사(Lighthouse > 옵션 > 접근성)를 실행하고 "0보다 큰 [tabindex] 값을 가진 요소가 없습니다"라는 감사 결과를 찾으세요.
"roving tabindex
"를 사용하여 액세스 가능한 구성 요소 생성 #
복잡한 구성 요소를 구축하는 경우 초점을 넘어 추가적인 키보드 지원을 추가해야 할 필요가 있을 수 있습니다. 내장된 select
요소를 고려하세요. 초점을 맞출 수 있으며 화살표 키를 사용하여 추가 기능(선택 가능한 옵션)을 표시할 수 있습니다.
고유한 구성 요소에서 유사한 기능을 구현하려면 "roving tabindex
"라는 기술을 사용하세요. roving tabindex는 현재 활성화된 하위 요소를 제외한 모든 하위 요소에 대해 tabindex
을 설정하여 작동합니다. 그런 다음 구성 요소는 키보드 이벤트 리스너를 사용하여 사용자가 눌렀던 키를 확인합니다.
이 경우 구성 요소는 이전에 포커스가 맞춰진 하위 요소의 tabindex
를 -1로 설정하고 포커스를 받을 하위 요소의 tabindex
를 0으로 설정한 다음 여기에 focus()
메서드를 호출합니다.
이전
<div role="toolbar">
<button tabindex="-1">Undo</div>
<button tabindex="0">Redo</div>
<button tabindex="-1">Cut</div>
</div>
이후
<div role="toolbar">
<button tabindex="-1">Undo</div>
<button tabindex="-1">Redo</div>
<button tabindex="0">Cut</div>
</div>
This HTML renders a modal dialog:
<div role="dialog" aria-labelledby="dialog-header">
<button aria-label="Close"></button>
<h2 id="dialog-header">
Do you want to allow notifications from this website?
</h2>
<button>No</button>
<button>Yes</button>
</div>
What is the tab order for the elements in the sample?
- The Close button
- The No button
- The Yes button
Only the <button>
elements are included in the tab order because they're the only standardized HTML form elements. To insert other elements into the tab order, you would add a tabindex
attribute.
<section tabindex="-1">
<h2>Cat facts</h2>
<ul>
<li>A group of cats is called a <a href="https://m-w.com/dictionary/clowder">clowder</a>.</li>
<li>Most cats are <a href="https://www.catfacts.org/catnip.html"> unaffected by catnip</a>.</li>
</ul>
</section>
Which elements from the sample are included in the tab order?
Only the <a>
elements are included in the tab order.
The <section>
element is not in the tab order because it has a negative tabindex
value. (It can, however, be focused using the focus()
method.) The tabindex
value for the <section>
element doesn't affect its children.
This HTML renders a popup menu followed by a search input:
<div role="menu" tabindex="0">
<a role="menuitem" href="/learn/" tabindex="-1">Learn</a>
<a role="menuitem" href="/measure/" tabindex="-1">Measure</a>
<a role="menuitem" href="/blog/" tabindex="-1">Blog</a>
<a role="menuitem" href="/about/" tabindex="-1">About</a>
</div>
<input tabindex="1" type="text" role="search" aria-label="Search" placeholder="Search">
Which element in the sample comes first in the tab order?
The Search text input comes first in the tab order. Because it has a tabindex
greater than zero, it jumps to the front of the tab order.
(This behavior is likely to cause confusion if the menu is positioned on the page before the search input. This is an example of why having a tabindex
value greater than zero is considered an anti-pattern.)
This HTML renders a custom radio group, which should have a roving tabindex
. (To keep things simpler, ignore the aria-*
attributes for now.)
<div role="radiogroup" aria-labelledby="breed-header">
<h3 id="breed-header">Your cat's breed</h3>
<div role="radio" aria-checked="false" tabindex="0">Persian</div>
<div role="radio" aria-checked="false" tabindex="-1">Bengal</div>
<div role="radio" aria-checked="false" tabindex="-1">Maine Coon</div>
</div>
When a role="radio"
element is focused, what should happen when a user presses the Right
arrow key?
- Change the
tabindex
values for all radio elements in the group to -1. - If there's a radio element after the one that's focused, set its
tabindex
value to 0. - If there's no radio element after the one that's focused, set the
tabindex
value of the first radio element in the group to 0. - Focus the radio element that now has a
tabindex
of 0.
That's a lot—and it doesn't even include ARIA attributes! This is an example of why it's easier to use built-in elements with built-in keyboard behavior whenever you can.
키보드 액세스 레시피 #
사용자 지정 구성 요소에 필요한 키보드 지원 수준이 확실하지 않은 경우 ARIA Authoring Practices 1.1을 참조하세요. 이 편리한 가이드는 일반적인 UI 패턴을 나열하고 구성 요소가 지원해야 하는 키를 식별합니다.