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>
造型
关于造型细节,还有一些需要了解的事项。
除了 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>
: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;
}
聚焦功能
实际上,我们的章节摘要包含两个截然不同的元素:“显示全部”按钮和评论者的头像。整个摘要都支持交互,支持多种输入方式和高亮显示。这本身就很有意义,也是使用语义化 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>
: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 */
其他需要考虑的因素还有user-select…… cursor。user-select尤其是……,这是一个开放性问题,所以我保留了它。我确实考虑了summary…… cursor: default,尽管优先级排序的方式肯定不止一种。
聚酯纤维填充物
Details 的 polyfill非常棒。它体积很小(不到 1.5kB),而且对 IE 和 Edge 浏览器的兼容性也很好。它也是一个分层 polyfill 的优秀范例,根据浏览器支持的不同,分别填充不同的功能。建议阅读一下源代码,我从中受益匪浅。
无障碍
Scott O'Hara 研究了各种屏幕阅读器和浏览器组合下 details 元素的使用情况。我强烈建议您阅读这篇文章以获取更多信息和分析结果。简而言之,屏幕阅读器的支持情况良好,但仍有一些特殊情况需要解决。
(我很乐意在亲自调查并检查 Scott 提到的 bug 的状态之后,提供更多信息。)
除了 Scott 的注释之外,关于可折叠部分,我还有几点要指出。
避免更改显示文本
您可能注意到,在上面的示例中,我并没有在展开时更改“显示全部”文本。您可以使用 JavaScript,甚至可以根据open属性条件使用一些 CSS 来实现这一点。但请注意!在切换按钮中,通常不会更改文本,因为状态已经通过某种方式传达aria-expanded(Heydon 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>
/* 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;
}
然而,考虑到ARIA 的使用规则,并权衡此功能的价值,我不会采纳它。“显示全部”图标保留在视觉上并无大碍,而且我喜欢这种统一的呈现方式。此外,箭头本身就是一个很好的指示,所以如果您需要此功能,请考虑不要移除它。
避免使用互动内容summary
摘要中的交互式内容目前仍是规范中的一个开放性问题。我也质疑这种做法的必要性。例如,我不会把一个元素放在button另一个元素里button,同样地,我也不会把交互式内容嵌套在摘要元素里。具体情况可能因人而异,如果你确实有这样的使用场景,这或许是一个重新设计组件的机会。
总结
details 元素是 HTML 的重要组成部分。它提供了很多常用功能,而无需依赖 JavaScript。这对于提高网站的稳定性至关重要。无论是请求失败,还是浏览器负载过重(例如加载服务器端渲染的标记时),能够无需依赖主脚本即可折叠部分内容都非常实用。
GitHub 对细节的运用还有更多有趣的地方,例如模态框和菜单。它本身也提供了一些样式钩子,甚至还有一些用户体验方面的问题值得探讨。这个 polyfill 非常全面,无障碍功能也相当完善。
您在项目中尝试过使用 details 元素吗?感觉如何?请告诉我!
更多资源
- 穆安的演讲和笔记引领我走上了这条道路。它提供了简洁明了的解释和指导,非常适合收藏和参考,比如在撰写文章时 :)
- MDN指南中关于细节部分有很好的总结(没有双关的意思)。
- 详细规格说明
- 用于详细信息的填充物
- 海顿·皮克林 (Heydon Pickering) 的《包容性组件》(Inclusive Components) 一书中有一章专门解释了可折叠部分 (Collapsible Sections) 的各种功能。他还详细介绍了如何使用 JavaScript 实现可折叠部分。强烈推荐阅读,内容非常精彩。
