原生 details + summary 是最轻量的展开方案,语义正确、可访问性好,默认折叠且点击自动切换;需避免 display: none 和错误 dom 结构,动画推荐用 max-height 过渡而非 height。

点击卡片触发 details 元素是最轻量的展开方案
不用写 JS、不依赖框架,原生 details + summary 就能实现基础展开收起。它语义正确、可访问性好(支持键盘 Enter 和 Space),且默认折叠,点击 summary 自动切换状态。
常见错误是直接用 div + 类名模拟,结果丢失焦点管理、屏幕阅读器无法识别状态变化。还有人给 details 加 display: none 强制隐藏——这会让整个元素不可见且不可交互,必须避免。
示例结构:
<details><summary>标题内容</summary><p>这里是展开后的详情文本。</p> </details>
-
summary必须是details的第一个子元素,否则不生效 - 默认箭头样式可通过
summary::marker覆盖,但 Safari 对该伪元素支持有限 - 想默认展开?加
open属性:<details open></details>
需要动画就得用 CSS 过渡 + max-height 或 height
details 本身不支持 CSS 动画(height: auto 无法过渡),所以得绕一下:用 max-height 模拟高度变化,配合 overflow: hidden 隐藏溢出内容。
关键点在于设定一个「足够大但不过分夸张」的 max-height 值。设太小会截断内容;设太大(比如 1000px)在收起时动画会明显变慢(因为要从 1000px 缩到 0)。
推荐做法:
- 给
details设置overflow: hidden - 给
details[open] > *(即展开时的子内容)设置max-height,例如max-height: 300px - 给非展开态设
max-height: 0,并加transition: max-height 0.3s ease-in-out - 注意:内容里如果有
padding或margin,需一并考虑进高度估算
JS 控制展开逻辑时,别直接操作 style.height
手动用 JS 改 element.style.height 看似直观,但容易出问题:内容高度动态变化(比如字体加载、图片加载后重排)、响应式断点切换、或者父容器 font-size 变化,都会让预设的像素值失效。
更稳妥的方式是用 getBoundingClientRect().height 或 offsetHeight 实时读取目标高度,再设为 style.height,动画结束后清空内联样式:
const content = card.querySelector('.card-content');
const height = content.scrollHeight;
content.style.height = '0';
content.offsetHeight; // 触发重排,确保样式已应用
content.style.height = height + 'px';
- 记得监听
transitionend清除height,否则后续 JS 获取高度会不准 - 如果卡片有多个,确保每次只操作当前点击项,避免
querySelectorAll误选 - 移动端要注意
scrollHeight在display: none下返回 0,得先临时设为block再读
动画卡顿或闪跳?检查是否触发了布局抖动
展开动画中频繁读写 offsetHeight / scrollHeight 并立刻设 style.height,容易引发强制同步布局(Layout Thrashing),尤其在低端设备上表现明显:先收起、再闪一下、最后展开。
解决方法很直接:
- 所有读操作(如
scrollHeight)放在一起,所有写操作(如style.height)放在一起 - 用
requestAnimationFrame分离读写时机,例如先读高宽,下一帧再写样式 - 避免在
transition过程中修改影响布局的属性(如padding、font-size) - 如果只是纯文字卡片,优先用
max-height方案,它不触发重排,性能更稳
细节往往藏在 DOM 更新节奏里,而不是动画时长或贝塞尔曲线。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











