tabindex
고친 사람 github-actions[bot]
tabindex 는 웹 페이지의 부품이 키보드 입력을 받을 수 있게 할지 정합니다. Tab 키를 누를 때 그 부품에 들르는지와 몇 번째로 들르는지도 정합니다. 마우스 없이 키보드만 쓰는 사람이 직접 만든 부품까지 닿게 해 줍니다.
쉽고 빠른 이해
tabindex 는 Tab 키가 들르는 부품의 명단을 고칩니다. 아무 뜻이 없는 <div> 태그로 직접 만든 버튼을 그 명단에
넣는 식입니다.
키보드만 쓰는 사람은 Tab 키로 부품 사이를 옮겨 다닙니다. 명단에 없는 부품에는 아무리 눌러도 닿지 않습니다.
- 0 을 적으면 문서에 적힌 순서대로 들릅니다
- -1 을 적으면 Tab 키는 안 들릅니다. 코드로는 키보드 입력을 그 부품으로 돌려 놓을 수 있습니다
- 1 이상을 적으면 다른 부품보다 먼저 들릅니다
대신 한계가 있습니다. tabindex 는 닿게만 해 줍니다. Enter 키로 눌리는 동작은 따로 짜야 합니다.
1 이상을 쓰면 화면의 순서와 Tab 키의 순서가 어긋납니다. 그래서 맞는 태그가 있으면 그 태그를 먼저 씁니다.
상세
아무 뜻이 없는 <div> 태그로 직접 만든 버튼 하나를 들고 갑니다. 먼저 키보드만 쓰는 사용자가 이 버튼에 왜 못 닿는지
봅니다. 그다음 tabindex 의 값 세 갈래가 이 버튼을 어떻게 바꾸는지 따라갑니다.
키보드로 페이지를 쓰는 사람
마우스를 못 쓰는 사람이 있습니다. 손을 움직이기 어려운 사람이 그렇습니다. 화면을 못 봐서 마우스 포인터가 어디 있는지 모르는 사람도 그렇습니다. 이들은 키보드만으로 페이지를 씁니다.
장애가 있어도 웹 페이지를 쓸 수 있게 만드는 일을 접근성이라고 합니다. 그중 키보드만으로 모든 기능을 쓸 수 있게 하는 일을 키보드 접근성이라고 부릅니다. tabindex 는 이 일을 돕는 도구입니다.
포커스와 Tab 키
키보드로 글자를 치면 그 글자는 화면의 한 부품으로만 갑니다. 지금 키보드 입력을 받는 부품을 포커스를 가졌다고 합니다. 한 페이지에서 포커스를 가진 부품은 언제나 하나뿐입니다.
포커스는 Tab 키로 옮깁니다. Tab 을 누르면 다음 부품으로 갑니다. Shift 와 Tab 을 함께 누르면 앞 부품으로 돌아갑니다. 브라우저는 포커스를 가진 부품에 테두리를 그려서 지금 어디에 있는지 보여 줍니다.
웹 페이지는 HTML(HyperText Markup Language)로 씁니다. 페이지의 부품 하나하나를 태그로 적습니다.
링크를 만드는 <a>, 버튼을 만드는 <button>, 입력 칸을 만드는 <input> 이 태그입니다.
Tab 키가 들르는 태그는 정해져 있습니다. 링크 · 버튼 · 입력 칸 · 선택 상자처럼 사용자가 만지라고 만든 태그들입니다. 이 태그들은 따로 적지 않아도 처음부터 포커스를 받습니다.
직접 만든 버튼이 안 닿는 까닭
<div> 는 아무 뜻이 없는 태그입니다. 모양을 꾸미기 쉬워서 버튼을 직접 만들 때 자주 씁니다.
클릭하면 자바스크립트 코드가 돌게 붙여 두면 마우스로는 잘 눌립니다.
그런데 <div> 는 포커스를 받는 태그가 아닙니다. Tab 키는 이 버튼을 건너뜁니다. 키보드만 쓰는
사용자에게는 이 버튼이 없는 것과 같습니다.
아래 둘째 줄이 <div> 로 만든 저장 버튼이라고 칩니다. 클릭하면 저장 코드가 돌게 붙여 두었습니다.
오른쪽 주석은 Tab 키로 닿는지입니다.
<button>저장</button> <!-- 닿는다 -->
<div>저장</div> <!-- 안 닿는다 -->
화면에서는 두 버튼이 똑같이 보일 수 있습니다. 차이는 키보드를 쓸 때만 드러납니다. 그래서 만든 사람이 마우스로만 시험하면 알아채지 못합니다.
속성 하나로 고치는 순서
속성은 태그에 덧붙여 성질을 바꾸는 이름과 값의 쌍입니다. <a href="/home"> 의 href 는 링크가
어디로 가는지 정하는 속성입니다.
tabindex 는 포커스를 받을지와 Tab 키가 들르는 순서를 정하는 속성입니다. 어느 태그에나 붙일 수 있어서 전역 속성이라고 부릅니다. 값은 정수 하나입니다.
값은 0 · 음수 · 양수의 세 갈래로 읽힙니다. 속성을 아예 안 적으면 브라우저가 태그 종류를 보고 정합니다. 아래 표가 네 경우를 견줍니다.
| 값 | 포커스를 받나 | Tab 키가 들르나 | 들르는 순서 |
|---|---|---|---|
| 안 적음 | 태그 종류에 따라 | 태그 종류에 따라 | 문서에 적힌 순서 |
| 0 | 받는다 | 들른다 | 문서에 적힌 순서 |
| 음수 | 받는다 | 안 들른다 | 없음 |
| 양수 | 받는다 | 들른다 | 다른 부품보다 먼저 |
세 갈래를 하나씩 봅니다.
문서 순서에 끼우는 0
tabindex="0" 은 포커스를 받지 않던 태그를 받게 만듭니다. 들르는 순서는 HTML 문서에 그 태그가
적힌 위치를 따릅니다. 버튼이나 링크 사이에 끼어 함께 차례를 기다리는 셈입니다.
앞의 <div> 버튼에 붙이면 이렇게 됩니다. 오른쪽 주석은 Tab 키를 눌렀을 때 들르는 차례입니다.
<button>취소</button> <!-- 1 -->
<div tabindex="0">저장</div> <!-- 2 -->
<a href="/help">도움말</a> <!-- 3 -->
<div> 가 이제 둘째로 닿습니다. 문서에서 취소 버튼과 도움말 링크 사이에 적혀 있기 때문입니다.
Tab 에서 빼는 음수
tabindex="-1" 은 포커스는 받게 하되 Tab 키는 들르지 않게 합니다. 음수면 -1 이든 -5 든 똑같이
동작합니다. 그래서 흔히 -1 을 씁니다.
-1 을 단 부품은 코드가 포커스를 직접 보낼 때 씁니다. 자바스크립트의 focus() 는 부품에
포커스를 보내는 함수입니다. 이 함수는 포커스를 받을 수 있는 부품에만 먹습니다.
흔한 쓰임은 스크린 리더 사용자에게 안내를 들려줄 때입니다. 스크린 리더는 화면의 글과 부품을 소리로 읽어 주는 프로그램입니다. 포커스가 옮겨 가면 그 부품을 먼저 읽습니다. 그래서 포커스를 보내는 일이 곧 사용자의 주의를 그곳으로 데려가는 일이 됩니다.
입력 양식의 오류 안내가 그런 예입니다. 사용자가 입력 칸을 비워 둔 채 제출을 누르면 페이지 위쪽에 안내 문단을 띄웁니다. 그 문단으로 포커스를 옮기면 스크린 리더가 안내를 바로 읽어 줍니다. 아래가 그 안내 문단입니다.
<p id="err" tabindex="-1">이메일을 적어 주세요</p>
위 문단은 평소 Tab 순서에 안 들어갑니다. 키보드 사용자가 양식을 돌아다닐 때 걸리지 않습니다. 오류가 났을 때만 아래 한 줄이 포커스를 그 문단으로 보냅니다.
document.getElementById("err").focus();
이미 포커스를 받는 태그에 -1 을 붙이면 Tab 순서에서 빠집니다. 마우스로는 전처럼 눌립니다.
앞줄로 당기는 양수
1 이상의 값은 그 부품을 Tab 순서의 앞줄로 당깁니다. 양수를 단 부품끼리는 작은 수부터 들릅니다. 값이 같으면 문서에 먼저 적힌 쪽이 먼저입니다.
앞줄을 다 들른 뒤에야 뒷줄로 넘어갑니다. 뒷줄은 0 을 단 부품과 원래 포커스를 받는 태그입니다. 이 뒷줄은 문서에 적힌 순서대로 들릅니다.
아래 네 버튼의 오른쪽 주석도 들르는 차례입니다.
<button>가</button> <!-- 3 -->
<button tabindex="2">나</button> <!-- 2 -->
<button tabindex="1">다</button> <!-- 1 -->
<button>라</button> <!-- 4 -->
문서에서는 가 · 나 · 다 · 라 순서입니다. Tab 키는 다 · 나 · 가 · 라 순서로 들릅니다. 양수 둘이 앞줄을 차지하고 나머지 둘이 문서 순서대로 뒤따릅니다.
Tab 순서를 정하는 규칙
지금까지 본 세 갈래를 한 부품의 처지에서 모으면 아래 그림이 됩니다. 부품 하나가 Tab 순서의 어디에 서는지를 위에서부터 묻습니다.
flowchart TD
A["부품 하나"] --> B{"원래 포커스를 받는 태그이거나<br/>tabindex 를 적었나"}
B -->|둘 다 아니다| X["Tab 키가 건너뛴다"]
B -->|그렇다| C{"tabindex 값"}
C -->|음수| X
C -->|양수| P["앞줄 · 작은 수부터"]
C -->|0 또는 안 적음| Q["뒷줄 · 문서 순서대로"]
첫 질문은 태그 종류와 tabindex 를 함께 봅니다. <div> 처럼 포커스를 안 받는 태그라도 tabindex 를
적었으면 다음 질문으로 넘어갑니다. 음수는 포커스를 받으면서도 건너뛰는 유일한 경우입니다.
양수를 피하는 까닭
양수를 쓰면 화면에서 위에 있는 부품이 Tab 순서에서는 뒤로 밀립니다. 키보드 사용자는 포커스 테두리가 페이지를 이리저리 튀는 것을 봅니다. 스크린 리더 사용자는 듣는 순서가 뒤섞입니다.
고치기도 어렵습니다. 양수를 하나 쓰면 그 뒤로 끼울 부품마다 숫자를 다시 매겨야 합니다. 페이지 여러 곳에서 숫자가 섞이면 전체 순서를 한눈에 볼 방법이 없습니다.
그래서 양수는 권하지 않습니다. Tab 순서를 바꾸고 싶으면 HTML 에 태그를 적는 순서를 바꿉니다. 화면 순서와 문서 순서를 맞추면 Tab 순서도 저절로 맞습니다.
묶음 안에서 버튼 하나만 여는 방법
글꼴을 고르는 버튼 열 개가 모인 도구 모음을 떠올립니다. 버튼마다 Tab 순서에 있으면 이 모음을 지나가는 데만 Tab 을 열 번 눌러야 합니다. 그래서 이런 묶음에는 Tab 키로 한 번만 들어가게 만듭니다. 안에서는 화살표 키로 옮겨 다니게 합니다.
이때 0 과 -1 을 함께 씁니다. 묶음 안에서 지금 고른 버튼 하나만 0 을 주고 나머지는 -1 을 줍니다. 아래는 도구 모음 안의 버튼 셋입니다.
<button tabindex="0">굵게</button>
<button tabindex="-1">기울임</button>
<button tabindex="-1">밑줄</button>
Tab 키는 「굵게」에만 들르고 다음 Tab 에서 도구 모음을 빠져나갑니다. 오른쪽 화살표를 누르면 코드가 「굵게」를 -1 로, 「기울임」을 0 으로 바꾸고 「기울임」에 포커스를 보냅니다.
이 방법을 로빙 tabindex라고 부릅니다. 0 을 가진 버튼 하나가 묶음 안을 돌아다닌다는 뜻입니다. 사용자가 나갔다 다시 들어오면 마지막으로 고른 버튼에 바로 닿는다는 이점도 따라옵니다.
tabindex 가 안 해 주는 일
tabindex 는 포커스를 받게만 합니다. 버튼처럼 동작하게 만들지는 않습니다. 아래 표는 진짜 버튼과
tabindex 를 단 <div> 를 견줍니다.
| 하는 일 | <button> |
<div tabindex="0"> |
|---|---|---|
| Tab 키로 닿는다 | ✓ | ✓ |
| Enter 나 스페이스 키에 눌린다 | ✓ | ✗ |
| 스크린 리더가 버튼이라고 읽는다 | ✓ | ✗ |
둘째 줄을 채우려면 키 입력을 받아 처리하는 코드를 자바스크립트로 따로 짭니다. Enter 나 스페이스 키가 눌리면 클릭했을 때와 같은 코드를 부르게 만드는 것입니다.
셋째 줄은 ARIA(Accessible Rich Internet Applications)가 맡습니다. ARIA 는 태그에 속성을 덧붙여
스크린 리더에게 부품의 뜻을 알리는 표준입니다. 그 속성 가운데 role 은 부품이 무슨 종류인지 알립니다.
role="button" 을 적으면 스크린 리더가 버튼이라고 읽습니다.
이 셋을 다 해야 <div> 가 겨우 <button> 을 흉내 냅니다. 그래서 맞는 HTML 태그가 있으면 그 태그를
먼저 씁니다. 뜻에 맞는 태그를 골라 쓰는 방식을 시맨틱 마크업이라고 합니다.
읽히게 하려고 붙이는 0
글 문단이나 제목에 tabindex="0" 을 붙여 스크린 리더가 읽게 하려는 경우가 있습니다. 이것은 필요
없습니다. 스크린 리더는 Tab 키와 따로 페이지의 글을 처음부터 차례로 읽어 나가는 방법을 갖고 있습니다.
오히려 키보드 사용자가 손해를 봅니다. Tab 을 누를 때마다 누를 수도 없는 문단에 들러야 합니다. 들른 곳이 무엇을 하는 부품인지도 알 수 없습니다.
쓰는 때와 안 쓰는 때
앞의 내용을 상황별로 모읍니다. 위에서부터 보고 맞는 줄에서 멈춥니다.
| 상황 | 쓰는 것 |
|---|---|
| 버튼 · 링크 · 입력 칸이 필요하다 | 그 HTML 태그. tabindex 를 안 붙인다 |
| 맞는 태그가 없는 부품을 직접 만든다 | tabindex="0" · 키 처리 코드 · role |
| 코드로만 포커스를 보낼 곳이 있다 | tabindex="-1" |
| 묶음 안을 화살표 키로 옮겨 다니게 한다 | 고른 버튼만 0 · 나머지는 -1 |
| Tab 순서를 바꾸고 싶다 | 태그를 적는 순서를 바꾼다. 양수를 안 쓴다 |
| 글을 스크린 리더가 읽게 하고 싶다 | 아무것도 안 붙인다 |
관련 항목
tabindex 가 속하는 상위 분류와 표준
HTML · 전역 속성 · 접근성 · 웹 접근성 · WCAG
tabindex 가 옮기고 다루는 포커스의 성질
포커스 · 포커스 순서 · 포커스 표시 · 포커스 관리 · focus-visible · 키보드 함정
tabindex 와 함께 직접 만든 부품을 완성하는 수단
role · ARIA · aria-label · 키보드 이벤트 · 이벤트 핸들러 · JavaScript
tabindex 보다 먼저 쓰는 HTML 태그
시맨틱 마크업 · button 요소 · 링크 · input 요소 · select 요소
tabindex 를 쓰는 부품 설계 방식
로빙 tabindex · aria-activedescendant · 도구 모음 · 탭 목록 · 모달 대화상자 · 건너뛰기 링크
tabindex 처럼 포커스가 닿는 범위를 바꾸는 수단
inert · disabled 속성 · 포커스 트랩 · aria-hidden
tabindex 가 길을 터 주는 보조 기술과 조작 방식
스크린 리더 · 보조 기술 · 키보드 접근성 · 키보드 내비게이션
다른 이름: tabindex 속성