ARIA 접근성 가이드

속성 · 관계

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 하나입니다. 해당 요소는 같은 문서에 있어야 하며, 포커스를 가진 요소의 자손이거나 소유된 요소여야 합니다.
(없음 / 빈 값) 기본값활성 자손이 없습니다. 목록이 닫혀 있거나 아직 항목을 고르지 않은 상태입니다.

사용 예

포커스는 목록에, 활성 항목은 속성으로

권장HTML
<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>

콤보박스: 입력 상자에 포커스, 팝업 옵션을 가리킴

권장HTML
<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를 제거합니다.

방향키로 활성 항목 바꾸기

JavaScript
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' });
});

포커스를 받을 수 없는 컨테이너

비권장HTML
<!-- 비권장: 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를 섞어 쓰지 않습니다.

사용할 수 있는 역할

이 속성을 쓰는 패턴

참고 자료