속성 · 관계
aria-activedescendant
DOM 포커스는 컨테이너에 둔 채, 그 안에서 현재 활성화된 항목이 무엇인지 ID로 알립니다. ("가상 포커스")
한눈에 보기
- 구분
- 속성 (Property)
- 분류
- 관계
- 값 형식
- ID 참조 (ID reference) — 하나
- 기본값
없음- 적용 범위
- 특정 역할에만 사용
- HTML 대체
<select>,<input list>+<datalist>는 브라우저가 활성 항목을 자체 관리합니다
설명
aria-activedescendant는 복합 위젯의 포커스 관리 방법 두 가지 중 하나입니다. 보통은 방향키를 누를 때마다 실제 DOM 포커스를 다음 항목으로 옮기는 "로빙 tabindex" 방식을 쓰지만, 이 속성을 쓰면 포커스는 컨테이너(예: listbox)나 입력 상자(예: combobox)에 그대로 두고, 속성 값만 바꿔 "지금 활성화된 항목은 이것"이라고 보조기술에 알립니다.
대표적인 곳은 콤보박스입니다. 사용자가 입력 상자에 글자를 계속 입력해야 하므로 포커스를 목록으로 옮길 수 없고, 대신 입력 상자의 aria-activedescendant가 팝업 목록 상자 안의 옵션 ID를 가리킵니다. 그 밖에 listbox, grid, tree, menu, tablist, toolbar 등 복합 위젯과 textbox, group, application에서 쓸 수 있습니다.
명세에 따라 참조 대상은 반드시 포커스를 가진 요소의 자손(또는 aria-owns로 소유한 요소)이어야 합니다. 예외는 combobox·textbox·searchbox가 aria-controls로 연결한 팝업 안의 항목을 가리키는 경우입니다. 속성을 가진 요소는 실제로 포커스를 받을 수 있어야 하므로 tabindex="0"(또는 네이티브 입력 요소)가 필요합니다.
DOM 포커스가 움직이지 않으므로 시각적인 포커스 표시를 직접 그려야 합니다(:focus 스타일이 항목에 적용되지 않음). 또한 활성 항목이 스크롤 영역 밖에 있으면 scrollIntoView()로 보이게 해야 합니다. 화면 확대 프로그램처럼 실제 포커스 위치를 따라가는 도구는 이 방식을 잘 따라가지 못하는 경우가 있어, 포커스를 옮길 수 있는 위젯이라면 로빙 tabindex가 더 단순할 수 있습니다.
검수할 때는 방향키를 누를 때마다 값이 실제로 존재하는 ID로 바뀌는지, 가리키는 요소에 접근 가능한 이름과 알맞은 역할(option 등)이 있는지, 활성 항목이 눈에 보이게 표시되는지, 목록이 닫힐 때 값이 제거되는지 확인합니다. 존재하지 않는 ID를 가리키면 화면낭독기는 아무것도 읽지 않습니다.
값
| 값 | 의미 |
|---|---|
<ID> | 현재 활성화된 자손 요소의 ID 하나입니다. 해당 요소는 같은 문서에 있어야 하며, 포커스를 가진 요소의 자손이거나 소유된 요소여야 합니다. |
(없음 / 빈 값) 기본값 | 활성 자손이 없습니다. 목록이 닫혀 있거나 아직 항목을 고르지 않은 상태입니다. |
사용 예
포커스는 목록에, 활성 항목은 속성으로
<ul role="listbox" tabindex="0" aria-label="배송 방법"
aria-activedescendant="opt-2">
<li id="opt-1" role="option" aria-selected="false">일반 택배</li>
<li id="opt-2" role="option" aria-selected="true">새벽 배송</li>
<li id="opt-3" role="option" aria-selected="false">당일 배송</li>
</ul>콤보박스: 입력 상자에 포커스, 팝업 옵션을 가리킴
<input type="text" role="combobox" aria-label="도시"
aria-expanded="true" aria-controls="city-list"
aria-autocomplete="list" aria-activedescendant="city-2">
<ul id="city-list" role="listbox" aria-label="도시 추천">
<li id="city-1" role="option" aria-selected="false">서울</li>
<li id="city-2" role="option" aria-selected="true">세종</li>
</ul>목록이 닫히면(aria-expanded="false") aria-activedescendant를 제거합니다.
방향키로 활성 항목 바꾸기
listbox.addEventListener('keydown', (e) => {
const options = [...listbox.querySelectorAll('[role="option"]')];
let i = options.findIndex(o => o.id === listbox.getAttribute('aria-activedescendant'));
if (e.key === 'ArrowDown') i = Math.min(options.length - 1, i + 1);
else if (e.key === 'ArrowUp') i = Math.max(0, i - 1);
else return;
e.preventDefault();
listbox.setAttribute('aria-activedescendant', options[i].id);
options[i].scrollIntoView({ block: 'nearest' });
});포커스를 받을 수 없는 컨테이너
<!-- 비권장: tabindex가 없어 포커스를 받을 수 없으므로 속성이 무의미 -->
<div role="listbox" aria-activedescendant="opt-1">
<div id="opt-1" role="option">일반 택배</div>
</div>직접 해 보기
목록에 Tab으로 들어간 뒤 위·아래 방향키를 눌러 보세요. 포커스는 목록에 머물고, aria-activedescendant가 가리키는 ID와 aria-selected가 바뀝니다.
배송 방법
- 일반 택배
- 새벽 배송
- 당일 배송
- 편의점 픽업
Tab으로 목록에 들어온 뒤 위·아래 방향키, Home, End로 이동합니다. DOM 포커스는 목록(ul)에 그대로 있고 aria-activedescendant 값만 바뀝니다.
실시간 코드 — 조작하면 속성 값이 바뀌는 모습을 볼 수 있습니다
화면낭독기에서는
| 상황 | 들리는 안내 (예) |
|---|---|
| 목록 상자에 Tab으로 진입 | 배송 방법, 목록, 일반 택배, 선택됨, 1/4 |
| 아래 방향키를 누름 | 새벽 배송, 선택됨, 2/4 |
| 존재하지 않는 ID를 가리키는 경우 | (아무것도 읽지 않음) |
사용 지침
이렇게 쓰세요
- 속성을 가진 요소가 실제로 포커스를 받을 수 있게 합니다(
tabindex="0"또는 입력 요소). - 활성 항목에 눈에 보이는 포커스 표시를 직접 스타일링합니다.
- 활성 항목이 스크롤 영역 밖에 있으면 화면 안으로 스크롤합니다.
- 팝업이 닫히거나 항목이 사라지면 속성을 제거하거나 빈 값으로 둡니다.
이렇게 쓰지 마세요
- 포커스를 가진 요소의 자손(또는 소유 요소)이 아닌 요소를 가리키지 않습니다(콤보박스의 팝업은 예외).
- 존재하지 않거나 숨겨진(
display:none) 요소의 ID를 남겨 두지 않습니다. - 같은 위젯 안에서 로빙 tabindex와
aria-activedescendant를 섞어 쓰지 않습니다.
사용할 수 있는 역할
관련 속성·패턴
이 속성을 쓰는 패턴