隠しコンテンツを検索

ウェブ インターフェースでは、ユーザー エクスペリエンスの向上、画面スペースの節約、ページ パフォーマンスの向上のために、コンテンツを非表示にすることがよくあります。display: none や visibility: hidden などの従来の方法では、コンテンツを視覚的に非表示にできますが、スクリーン リーダーやブラウザの「ページ内検索」などの機能でもコンテンツにアクセスできなくなります。

コンテンツを視覚的に非表示にしながら、ユーザーが検索できるようにし、URL フラグメントと「Scroll to Text Fragment」リンクを介してディープリンクできるようにするには、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 状態を同期する必要があります。

<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 アンカー フラグメントを使用して、ユーザーがそれぞれのセクションに直接移動できるようにする必要があります。