发布于 2026-01-06 2 阅读
0

HTML 细节非常棒

HTML 细节非常棒

原文发布于https://fotis.xyz/posts/details-is-awesome/

前段时间,我阅读了Mu-An Chiou题为“<details> 的细节”的演讲幻灯片和笔记。这篇演讲对如何使用<details>HTML 的 <details> 元素进行了精彩的概述。

简而言之,details`<collaborable section>` 及其配套组件 `<collaborable section>`summary提供了一种无需依赖 JavaScript 即可实现原生可折叠部分的方法。切换内容是网页上最常见的操作之一,因此能够无需脚本即可实现此功能非常吸引人。但它的功能不仅限于可折叠部分;您还可以用它创建面板、菜单和其他富有创意的东西!

我一直把这些知识记在心里。几周前,我在这篇博客上实现网页提及的可折叠部分时:

一个“网络提及”部分,带有一个按钮,可显示所有评论。

这似乎是使用 details 属性的绝佳案例。我既可以获得切换的原生功能,又可以对其进行样式设置。我们来看看如何实现它。

实现“显示全部”部分

标记

你嵌套在`.`<summary>内的元素会显示为“切换按钮”,无论该部分是否折叠。其后的所有内容都将被隐藏,仅在小部件展开时显示。<details><summary>

以下是我们评论区的初步设计示例:

<details>
  <summary>
    Show all (2)
  </summary>
  <ol>
    <li>I am a comment</li>
    <li>I am another comment.</li>
  </ol>
</details>
Enter fullscreen mode Exit fullscreen mode

造型

关于造型细节,还有一些需要了解的事项。

除了 Chrome 浏览器之外,箭头的样式都是通过` list-style<div>` 属性设置的,Chrome 浏览器使用的是`<div>`。您可以考虑重置这些属性,然后使用统一的选择器。summary::-webkit-details-marker::before

open当摘要打开时,会添加此属性。您可以查询该属性以details[open]进一步设置容器样式。MDN上的示例展示了 open 属性的实际应用。

我还发现,summary即使在 CSS 中定义了 flexbox 容器,Safari 浏览器也无法使用该元素。我原本想在“显示全部”文本旁边显示作者图片。为此,我在 summary 元素内部添加了一个额外的 div 容器,作为 flexbox 容器。

在为我们的细节添加更多样式后,它看起来会像这样:

<details class="details-reset">
  <summary class="summary fg-highlight cursor-default">
    <div class="flex-wrapper-for-safari">
      <span>Show all (2)</span>
      <div class="author-list">
        <div class="author">
          <img src="https://fotis.xyz/img/logo.png" alt="" class="author-image" />
        </div>
        <div class="author">
          <img src="https://fotis.xyz/img/logo.png" alt="" class="author-image" />
        </div>
      </div>
    </div>
  </summary>

  <ol>
    <li>I am a comment</li>
    <li>I am another comment</li>
  </ol>
</details>
Enter fullscreen mode Exit fullscreen mode
:root {
  --highlight: #da0993;
}

/**
  * Styles and resets for the <details> element.
  * @see https://github.com/primer/primer/blob/master/modules/primer-buttons/lib/button.scss#L206-L213
*/

/* Remove marker added by the display: list-item browser default */
.details-reset > summary {
  list-style: none;
}

/* Remove marker added by details polyfill */
.details-reset > summary::before {
  display: none;
}

/* Remove marker added by Chrome */
.details-reset > summary::-webkit-details-marker {
  display: none;
}

/* Other styles */
.flex-wrapper-for-safari {
  display: flex;
  align-items: center;
}

.fg-highlight {
  color: var(--highlight);
}

.cursor-default {
  cursor: default;
}

.author-list {
  display: flex;
  margin: 1rem;
}

.author {
  width: 2rem;
  height: 2rem;
}

.author-image {
  max-width: 100%;
  border-radius: 100%;
  background-color: gray;
}
Enter fullscreen mode Exit fullscreen mode

聚焦功能

实际上,我们的章节摘要包含两个截然不同的元素:“显示全部”按钮和评论者的头像。整个摘要都支持交互,支持多种输入方式和高亮显示。这本身就很有意义,也是使用语义化 HTML 的优势所在。

我认为我们可以通过增强焦点效果来做得更好,使其更清晰地突出显示操作。如果用户(通过键盘或其他方式)将焦点放在某个部分,我希望只突出显示“显示全部”部分。这种“嵌套焦点”模式是我从Heydon Pickering 的《可折叠部分》一文中借鉴的。

<details class="details-reset">
  <summary class="nested-focus fg-highlight cursor-default">
    <div class="flex-wrapper-for-safari">
      <span class="nested-focus-target">Show all (2)</span>
      <div class="author-list">
        <div class="author">
          <img src="https://fotis.xyz/img/logo.png" alt="" class="author-image" />
        </div>
        <div class="author">
          <img src="https://fotis.xyz/img/logo.png" alt="" class="author-image" />
        </div>
      </div>
    </div>
  </summary>

  <ol>
    <li>I am a comment</li>
    <li>I am another comment</li>
  </ol>
</details>
Enter fullscreen mode Exit fullscreen mode
:root {
  --highlight: #da0993;
}

.nested-focus:focus {
  /* NOTE: The only good reason to hide an outline is if you enhance it */
  outline: none;
}

.nested-focus:focus .nested-focus-target {
  outline: 2px solid var(--highlight)
}

/** The rest is the same as above */
Enter fullscreen mode Exit fullscreen mode

其他需要考虑的因素还有user-select…… cursoruser-select尤其是……,这是一个开放性问题,所以我保留了它。我确实考虑了summary…… cursor: default,尽管优先级排序的方式肯定不止一种。

聚酯纤维填充物

Details 的 polyfill非常棒。它体积很小(不到 1.5kB),而且对 IE 和 Edge 浏览器的兼容性也很好。它也是一个分层 polyfill 的优秀范例,根据浏览器支持的不同,分别填充不同的功能。建议阅读一下源代码,我从中受益匪浅。

无障碍

Scott O'Hara 研究了各种屏幕阅读器和浏览器组合下 details 元素的使用情况。我强烈建议您阅读这篇文章以获取更多信息和分析结果。简而言之,屏幕阅读器的支持情况良好,但仍有一些特殊情况需要解决。

(我很乐意在亲自调查并检查 Scott 提到的 bug 的状态之后,提供更多信息。)

除了 Scott 的注释之外,关于可折叠部分,我还有几点要指出。

避免更改显示文本

您可能注意到,在上面的示例中,我并没有在展开时更改“显示全部”文本。您可以使用 JavaScript,甚至可以根据open属性条件使用一些 CSS 来实现这一点。但请注意!在切换按钮中,通常不会更改文本,因为状态已经通过某种方式传达aria-expandedHeydon Pickering 曾撰文讨论过切换按钮)。我认为带有 details 元素的折叠部分也应该遵循同样的原则,因为它们的状态已经通过原生方式传达。

如果我想更改显示文本,我会尽量始终向屏幕阅读器显示相同的文本,并与切换按钮保持一致。

<details>
  <summary>
    <!-- This is exposed to AT -->
    <span class="visually-hidden">Show all (2)</span>
    <!-- These will be hidden from AT -->
    <span aria-hidden="true" class="summary-open">Hide all</span>
    <span aria-hidden="true" class="summary-closed">Show all (2)</span>
  </summary>
  <ul>
    <li>I am a comment</li>
    <li>I am another comment.</li>
  </ul>
</details>
Enter fullscreen mode Exit fullscreen mode
/* Hide visually, but keep in the tree for AT */
.visually-hidden {
  position: absolute !important;
  height: 1px; width: 1px;
  overflow: hidden;
  clip: rect(1px 1px 1px 1px);
  clip: rect(1px, 1px, 1px, 1px);
}

details .summary-open {
  display: none;
}
details[open] .summary-open {
  display: inline;
}
details .summary-closed {
  display: inline;
}
details[open] .summary-closed {
  display: none;
}
Enter fullscreen mode Exit fullscreen mode

然而,考虑到ARIA 的使用规则,并权衡此功能的价值,我不会采纳它。“显示全部”图标保留在视觉上并无大碍,而且我喜欢这种统一的呈现方式。此外,箭头本身就是一个很好的指示,所以如果您需要此功能,请考虑不要移除它。

避免使用互动内容summary

摘要中的交互式内容目前仍是规范中的一个开放性问题。我也质疑这种做法的必要性。例如,我不会把一个元素放在button另一个元素里button,同样地,我也不会把交互式内容嵌套在摘要元素里。具体情况可能因人而异,如果你确实有这样的使用场景,这或许是一个重新设计组件的机会。

总结

details 元素是 HTML 的重要组成部分。它提供了很多常用功能,而无需依赖 JavaScript。这对于提高网站的稳定性至关重要。无论是请求失败,还是浏览器负载过重(例如加载服务器端渲染的标记时),能够无需依赖主脚本即可折叠部分内容都非常实用。

GitHub 对细节的运用还有更多有趣的地方,例如模态框和菜单。它本身也提供了一些样式钩子,甚至还有一些用户体验方面的问题值得探讨。这个 polyfill 非常全面,无障碍功能也相当完善。

您在项目中尝试过使用 details 元素吗?感觉如何?请告诉我!

更多资源

文章来源:https://dev.to/isfotis/html-details-is-awesome-1c20