숨겨진 콘텐츠 검색

웹 인터페이스는 사용자 환경을 개선하거나, 화면 공간을 절약하거나, 페이지 성능을 높이기 위해 숨겨진 콘텐츠를 숨기는 경우가 많습니다. display: none 또는 visibility: hidden과 같은 기존 방법은 콘텐츠를 시각적으로 숨기는 데 효과적이지만, 스크린 리더와 '페이지에서 찾기'와 같은 브라우저 기능에서 해당 콘텐츠에 완전히 액세스할 수 없게 만듭니다.

콘텐츠를 시각적으로 숨기면서도 사용자가 검색할 수 있도록 하고 URL 프래그먼트 및 '텍스트 프래그먼트로 스크롤' 링크를 통해 딥 링크를 사용 설정하려면 HTML <details> 요소 또는 hidden="until-found" 속성을 사용하면 됩니다. <details> 요소는 구현 및 유지관리가 더 간단하므로 일반적으로 권장되지만, <details>로는 충분하지 않고 hidden="until-found"가 필요한 더 복잡한 경우도 있습니다.

예를 들면 다음과 같습니다.

  • 표시/숨기기 메커니즘의 스타일을 완전히 제어하려는 경우
  • 콘텐츠를 표시하거나 숨기는 UI 컨트롤이 DOM의 다른 부분에 있는 경우
  • 콘텐츠가 표시된 후 숨기기를 지원하지 않으려는 경우

구현 방법

<details> 요소에는 기본적으로 검색 및 액세스 가능한 텍스트가 있으며 특별한 구현이 필요하지 않습니다. 가능하면 hidden="until-found"보다 <details>를 사용하는 것이 좋습니다.

대신 hidden="until-found"를 사용해야 하는 경우 다음 안내를 따르세요.

  1. 속성 적용: 표시되지 않아야 하는 콘텐츠가 포함된 요소에 hidden="until-found" HTML 속성을 직접 추가합니다.
  2. UI 상태 동기화: 인터페이스에 콘텐츠의 공개 상태에 따라 달라지는 관련 상태가 있는 경우 (예: ARIA 속성 업데이트, 열기/닫기 CSS 클래스 전환, 아코디언 아이콘 회전) 다음을 수행합니다.
    • beforematch 이벤트에 대한 이벤트 리스너를 추가해야 합니다.
    • hidden="until-found" 속성이 있는 요소에 직접 beforematch 이벤트 리스너를 등록합니다. 이벤트가 버블링되므로, 상위 요소 (예: 탭 컨테이너)에 단일 리스너를 등록하여 여러 숨겨진 섹션을 한 번에 관리하는 이벤트 위임을 사용할 수도 있습니다.
    • 이벤트 리스너 내에서 관련 UI 요소를 동기화하는 로직을 실행합니다 (예: 열려 있는 다른 탭을 닫거나 전환 버튼의 상태를 변경).

예시 코드

목표: 사용자가 해당 섹션 내에서 단어를 검색할 때까지 콘텐츠가 사용자에게 표시되지 않는 숨겨진 컨테이너를 렌더링합니다.

<details> 요소 사용

<details>
  <summary>Click to expand</summary>
  <p>This content is visually hidden.</p>
</details>

상호 배타적 공개

상호 배타적인 콘텐츠 영역(예: 배타적 아코디언)을 처리할 때는 공유 name 속성과 함께 네이티브 HTML <details> 요소를 사용합니다.

<div class="accordion-group">
  <!-- The name attribute creates an exclusive disclosure group -->
  <details class="disclosure" name="my-accordion">
    <summary>Section 1</summary>
    <p>Section 1 content</p>
  </details>

  <details class="disclosure" name="my-accordion" open>
    <summary>Section 2</summary>
    <p>Section 2 content</p>
  </details>

  <details class="disclosure" name="my-accordion">
    <summary>Section 3</summary>
    <p>Section 3 content</p>
  </details>
</div>

hidden="until-found" 속성 사용

<!-- The browser automatically removes hidden="until-found" upon a search match -->
<div class="hidden-container" hidden="until-found">
  <p>This content is visually hidden.</p>
</div>

상호 배타적인 맞춤 공개 문구

외부 버튼으로 제어되는 맞춤 상호 배타적 영역을 처리할 때는 일치하는 패널만 표시되도록 합니다. beforematch 이벤트 핸들러에서 제어 버튼에 aria-expanded="true"를 설정하는 것과 같은 관련 ARIA 상태를 동기화해야 합니다(MUST).

<div class="custom-accordion">
  <div class="controls">
    <button aria-expanded="true" aria-controls="panel-1" id="btn-1">Section 1</button>
    <button aria-expanded="false" aria-controls="panel-2" id="btn-2">Section 2</button>
  </div>

  <div id="panel-1" class="panel">
    <p>Section 1 content (visible)</p>
  </div>

  <div id="panel-2" class="panel" hidden="until-found">
    <p>Section 2 content (hidden)</p>
  </div>
</div>
const accordion = document.querySelector('.custom-accordion');

accordion.addEventListener('beforematch', (e) => {
  // Hide all panels and synchronize button states before the browser reveals the matched panel
  accordion.querySelectorAll('.panel').forEach((panel) => {
    if (panel !== e.target) {
      panel.hidden = 'until-found';
    }
  });
  accordion.querySelectorAll('button').forEach((btn) => {
    const controls = btn.getAttribute('aria-controls');
    btn.setAttribute('aria-expanded', controls === e.target.id ? 'true' : 'false');
  });
});

hidden="until-found" 관련 권장사항

  • hidden="until-found" 속성이 있는 요소에 직접 적용하는 대신 중첩된 하위 래퍼에 테두리, 패딩, 배경을 적용해야 합니다. 이렇게 하면 요소가 숨겨져 있는 동안 의도하지 않은 레이아웃 변경이나 시각적 잔여물이 방지됩니다.
  • display: none, visibility: hidden 또는 연결된 display 또는 visibility CSS 속성을 hidden="until-found" 속성이 있는 요소에 직접 적용하지 마세요. 이렇게 하면 기본 기능이 중단되고 검색 색인에서 콘텐츠가 영구적으로 숨겨집니다.
  • 민감한 정보, 내부 데이터 토큰 또는 검색을 통해 노출되어서는 안 되는 관련 없는 데이터에는 hidden="until-found"를 사용하지 마세요.
  • hidden="until-found"를 '스크린 리더 전용' (.sr-only) 텍스트 대신 사용하지 마세요.

브라우저 지원 및 대체 전략

브라우저 지원이 누락되었다고 해서 hidden="until-found"를 피하지 마세요. 접근성 이점이 대체 구현 비용보다 훨씬 크기 때문입니다.

hidden="until-found" 대체

아코디언 또는 '더보기' 섹션과 같은 표준 UI 요소의 경우 JavaScript를 사용하여 기능을 감지하고 기능이 지원되지 않는 경우 모든 콘텐츠를 표시합니다.

if (!('onbeforematch' in HTMLElement.prototype)) {
  // Expand all hidden content for unsupported browsers
  document.querySelectorAll('[hidden="until-found"]').forEach((el) => {
    el.removeAttribute('hidden');
    // MANDATORY: also update any aria references to this element.
  });
}

상호 배타적인 UI 패러다임 (예: 콘텐츠가 동일한 시각적 영역을 공유하는 맞춤 전용 패널)의 경우 대체는 기본 상호작용 영역 아래에 모든 콘텐츠를 선형으로 추출하고 표시해야 하며, URL 앵커 프래그먼트를 사용하여 사용자가 각 섹션으로 직접 이동할 수 있도록 해야 합니다.