用语义化组织faq,原生支持展开收起、键盘操作和seo;需为每个设唯一id以支持url锚点跳转,并用脚本自动展开及聚焦;动画推荐clip-path而非height,嵌套需谨慎。

怎么用语义化标签组织FAQ列表
浏览器和屏幕阅读器靠HTML结构理解内容层级。用 <details></details> + <summary></summary> 是目前最轻量、原生支持展开收起的方案,不需要JS也能工作,且SEO友好。
- 不要用
<div> 套 <code><h3></h3>+<p></p>模拟折叠——它对辅助技术不透明,也不响应:focus-within或键盘操作 -
<details></details>默认可聚焦、空格/回车触发切换,<summary></summary>自动获得role="button" - 如果需要兼容IE(已无必要),才考虑降级为
<section></section>+ JS控制aria-expanded和hidden属性
<details> <summary>为什么页面加载慢?</summary> <p>检查是否在 <code><head></code> 中加载了未压缩的 JS 文件。</p> </details>
如何让FAQ支持锚点跳转和URL定位
用户点击分享链接如 faq.html#why-slow 时,浏览器应自动滚动到对应问题并展开。这依赖 ID 绑定和脚本微调。
- 每个
<details></details>必须有唯一id,且<summary></summary>内容或父级需能被document.getElementById()定位 - 浏览器原生不会自动展开带 hash 的
<details></details>,需加一行脚本监听hashchange和初始加载:
function openHashDetails() {
const id = location.hash.slice(1);
const el = document.getElementById(id);
if (el && el.tagName === 'DETAILS') el.open = true;
}
openHashDetails();
window.addEventListener('hashchange', openHashDetails);- 注意:ID 值不能含空格或特殊字符;若用中文标题生成 ID,建议转成小写拼音或用
encodeURIComponent()处理后再截取
为什么用CSS而不是JS控制展开动画
<details></details> 的 open 属性是布尔值,本身不支持过渡。强行用JS切换class做height动画,容易因内容高度动态变化导致抖动或计算错误。
立即学习“前端免费学习笔记(深入)”;
- 正确做法是用
details[open] summary ~ *配合max-height+transition,但只适用于内容高度相对固定的情况 - 更稳妥的是用
@starting-style(目前仅Chrome 119+)或改用clip-path动画,避免重排:
details summary ~ * {
clip-path: inset(0 0 100% 0);
transition: clip-path 0.25s ease;
}
details[open] summary ~ * {
clip-path: inset(0);
}- 别给
<details></details>直接设overflow: hidden——它会裁掉<summary></summary>的下边框或阴影,视觉割裂
怎么处理嵌套FAQ或复杂内容块
<details></details> 允许嵌套,但超过两层后键盘导航和焦点管理会变混乱,尤其当内部有表单控件(如 <input>、<select></select>)时。
- 嵌套时,外层
<details></details>的open状态不应影响内层默认状态;不要用JS联动开关 - 若FAQ项里要放代码块、表格或图片,请确保它们不破坏
<details></details>的流式布局:给<pre class="brush:php;toolbar:false;"></pre>加overflow-x: auto,给<table> 加 <code>width: 100% - 避免在
<summary></summary>里放<button></button>或<a></a>——会导致双击行为冲突(一次是展开,一次是按钮点击)
实际中最容易被忽略的,是 hash 定位后没触发 focus,导致键盘用户不知道当前在哪。补上 el.focus() 在 openHashDetails() 里,比动画细节重要得多。











