details标签原生支持展开/收起,需包含唯一summary子元素作为标题,其余内容为折叠区;可配合CSS美化样式、JS处理锚点跳转,并兼顾SEO与无障碍访问。

details标签的基本用法和默认行为details 标签原生支持展开/收起,不需要 JS 就能工作,但默认不带箭头(部分浏览器会渲染小三角,但不可控)。它语义明确,适合放用户协议这类「可选阅读」内容。注意:details 必须包含一个 summary 元素作为第一子节点,否则无法触发交互;浏览器会把 summary 渲染为可点击标题,其余子节点就是折叠区内容。
-
summary 内容会被朗读器识别为控件名称,建议写清用途,比如“查看完整用户协议”
- 不要嵌套多个
summary,只允许一个
- 如果协议里有
h2、ol 等块级元素,直接放进去完全合法,details 不限制子元素类型
让协议折叠区更易读的 CSS 调整
原生 details 的样式非常简陋:文字无缩进、段落间距塌陷、没有边框或背景区分。用户点开后可能分不清哪段属于哪条条款。加几行 CSS 就能改善:
details {
border: 1px solid #e0e0e0;
border-radius: 4px;
margin-bottom: 1rem;
}
details[open] {
background-color: #f9f9f9;
}
summary {
padding: 0.75rem 1rem;
font-weight: 600;
cursor: pointer;
}
summary::marker {
content: "▸ ";
}
summary::-webkit-details-marker {
display: none;
}
-
summary::marker 和 summary::-webkit-details-marker 是为了统一箭头样式,避免 Safari 和 Chrome 表现不一致
- 不要用
display: none 隐藏整个 summary,否则键盘用户无法聚焦操作
- 如果协议含链接或按钮,确保它们在
[open] 状态下仍可访问(默认没问题)
处理协议中需要锚点跳转的场景
用户协议常有“参见第 3.2 条”这类内部引用,但 details 折叠时,目标 id 元素不可见,scrollIntoView() 可能失效或滚动错位。解决方案不是禁用折叠,而是补一手 JS:
- 给每个条款加唯一
id,例如 <p id="clause-3-2">...
- 监听
details 的 toggle 事件,在展开时检查 URL hash 是否匹配其子元素 id
- 匹配成功则调用
element.scrollIntoView({ block: 'center' }),并确保父 details 已 open
document.querySelectorAll('details').forEach(d => {
d.addEventListener('toggle', () => {
if (d.open && location.hash && d.contains(document.querySelector(location.hash))) {
document.querySelector(location.hash)?.scrollIntoView({ block: 'center' });
}
});
});
- 不要在页面加载时自动展开所有
details,这违背了“按需加载”的初衷
-
toggle 事件比 click 更可靠,它涵盖键盘空格/回车触发
SEO 和无障碍注意事项
搜索引擎能正常索引 details 内所有文本,无论是否 open,这点不用担心。但屏幕阅读器对折叠状态的提示依赖浏览器实现,有些只读 summary,有些会额外播报“已折叠”。
- 在
summary 后显式加状态提示更稳妥,比如“(已折叠)”或“(展开查看)”,用 aria-live="polite" 动态更新
- 不要给
details 加 role="region" 或其他冗余 ARIA 属性,反而干扰默认行为
- 如果协议内容极长(超 1000 行),考虑服务端分片或前端懒加载,单纯靠
details 折叠不能减少 DOM 体积或首屏渲染压力
summary 内容会被朗读器识别为控件名称,建议写清用途,比如“查看完整用户协议”summary,只允许一个h2、ol 等块级元素,直接放进去完全合法,details 不限制子元素类型details 的样式非常简陋:文字无缩进、段落间距塌陷、没有边框或背景区分。用户点开后可能分不清哪段属于哪条条款。加几行 CSS 就能改善:
details {
border: 1px solid #e0e0e0;
border-radius: 4px;
margin-bottom: 1rem;
}
details[open] {
background-color: #f9f9f9;
}
summary {
padding: 0.75rem 1rem;
font-weight: 600;
cursor: pointer;
}
summary::marker {
content: "▸ ";
}
summary::-webkit-details-marker {
display: none;
}-
summary::marker和summary::-webkit-details-marker是为了统一箭头样式,避免 Safari 和 Chrome 表现不一致 - 不要用
display: none隐藏整个summary,否则键盘用户无法聚焦操作 - 如果协议含链接或按钮,确保它们在
[open]状态下仍可访问(默认没问题)
处理协议中需要锚点跳转的场景
用户协议常有“参见第 3.2 条”这类内部引用,但 details 折叠时,目标 id 元素不可见,scrollIntoView() 可能失效或滚动错位。解决方案不是禁用折叠,而是补一手 JS:
- 给每个条款加唯一
id,例如 <p id="clause-3-2">...
- 监听
details 的 toggle 事件,在展开时检查 URL hash 是否匹配其子元素 id
- 匹配成功则调用
element.scrollIntoView({ block: 'center' }),并确保父 details 已 open
document.querySelectorAll('details').forEach(d => {
d.addEventListener('toggle', () => {
if (d.open && location.hash && d.contains(document.querySelector(location.hash))) {
document.querySelector(location.hash)?.scrollIntoView({ block: 'center' });
}
});
});
- 不要在页面加载时自动展开所有
details,这违背了“按需加载”的初衷
-
toggle 事件比 click 更可靠,它涵盖键盘空格/回车触发
SEO 和无障碍注意事项
搜索引擎能正常索引 details 内所有文本,无论是否 open,这点不用担心。但屏幕阅读器对折叠状态的提示依赖浏览器实现,有些只读 summary,有些会额外播报“已折叠”。
- 在
summary 后显式加状态提示更稳妥,比如“(已折叠)”或“(展开查看)”,用 aria-live="polite" 动态更新
- 不要给
details 加 role="region" 或其他冗余 ARIA 属性,反而干扰默认行为
- 如果协议内容极长(超 1000 行),考虑服务端分片或前端懒加载,单纯靠
details 折叠不能减少 DOM 体积或首屏渲染压力
id,例如 <p id="clause-3-2">...
details 的 toggle 事件,在展开时检查 URL hash 是否匹配其子元素 id
element.scrollIntoView({ block: 'center' }),并确保父 details 已 open
details,这违背了“按需加载”的初衷toggle 事件比 click 更可靠,它涵盖键盘空格/回车触发details 内所有文本,无论是否 open,这点不用担心。但屏幕阅读器对折叠状态的提示依赖浏览器实现,有些只读 summary,有些会额外播报“已折叠”。
- 在
summary后显式加状态提示更稳妥,比如“(已折叠)”或“(展开查看)”,用aria-live="polite"动态更新 - 不要给
details加role="region"或其他冗余 ARIA 属性,反而干扰默认行为 - 如果协议内容极长(超 1000 行),考虑服务端分片或前端懒加载,单纯靠
details折叠不能减少 DOM 体积或首屏渲染压力
details 是个轻量解法,但它不解决内容组织逻辑——条款编号是否连续、跳转是否准确、移动端触控区域够不够大,这些得靠结构设计和测试来兜底。



















