搜索隐藏的内容

网页界面通常会隐藏内容,以改善用户体验、节省屏幕空间或提升网页性能。display: none 或 visibility: hidden 等传统方法可以从视觉上隐藏内容,但也会使屏幕阅读器和“在页面中查找”等浏览器功能完全无法访问这些内容。

若要隐藏内容,但仍允许用户搜索这些内容,并允许通过网址片段和“滚动到文本片段”链接深层链接到这些内容,您可以使用 HTML <details> 元素或 hidden="until-found" 属性。通常建议使用 <details> 元素,因为它更易于实现和维护,但在一些更复杂的情况下,<details> 并不够用,需要使用 hidden="until-found"。

例如:

  • 如果您想完全控制显示/隐藏机制的样式。
  • 如果用于显示/隐藏内容的界面控件位于 DOM 的其他部分。
  • 如果您不想支持在内容显示后将其隐藏。

如何实现

<details> 元素默认具有可搜索和可访问的文本,无需进行特殊实现。如果可能,请优先使用 <details>,而不是 hidden="until-found"。

如果您需要改用 hidden="until-found",请按照以下说明操作:

  1. 应用属性:直接将 hidden="until-found" HTML 属性添加到包含应隐藏的内容的元素中。
  2. 同步界面状态:如果界面具有依赖于内容可见性的相关状态(例如,更新 ARIA 属性、切换打开/关闭 CSS 类或旋转折叠面板图标),请执行以下操作:
    • 您必须为 beforematch 事件添加事件监听器。
    • 直接在带有 hidden="until-found" 属性的元素上注册 beforematch 事件监听器。由于事件会冒泡,您也可以使用事件委托,在父元素(例如标签页容器)上注册单个监听器,以便一次性管理多个隐藏部分。
    • 在事件监听器中,执行逻辑以同步相关界面元素(例如关闭其他打开的标签页或更改切换按钮的状态)。

示例代码

目标:呈现一个隐藏的容器,其中的内容对用户保持不可见,直到用户在该部分中搜索某个字词。

使用 <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 状态,例如在控制按钮上设置 aria-expanded="true"。

<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" 后备

对于标准界面元素(例如折叠式菜单或“阅读更多”部分),请使用 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.
  });
}

对于互斥的界面范式(例如内容共享同一视觉区域的自定义互斥面板),后备方案应提取所有内容,并将其线性显示在主要互动区域下方,同时使用网址锚点片段让用户能够直接导航到相应部分。