先记住这个答案
已知高度时设置height从0到具体值,配合transition: height即可平滑过渡;未知高度时传统做法是给max-height设一个足够大的值,从0过渡到该值,但实际计时与内容高度不完全匹配;更优做法是使用grid-template-rows从0fr到1fr,overflow:hidden,无需预估高度。
- 已知高度用height过渡最可靠
- 未知高度可用grid-template-rows实现
- max-height技巧有计时不准的代价
height过渡的底层限制与机理
CSS过渡要求起始和结束值都是可计算的具体数值。对于height: auto,浏览器在过渡开始时不知道目标像素值,因此不触发插值,状态瞬间切换。height: 0到auto没有明确to值,因此不会触发过渡动画。
已知内容高度时,我们通过JS测量或固定高度给出px值,浏览器就能逐帧计算差值。未知高度时,max-height技巧是设置一个足够大的max-height值作为伪目标,让元素实际高度受内容约束,但计算出的过渡时间是按设定的max-height计算,导致视觉上先快后慢或提前结束。
折叠面板在动态内容下切换
假设一个FAQ列表,点击问题展开答案,答案文本可能随用户操作改变(如加载更多、转译)。内容高度未知,且每次点击都可能不同。采用grid行方案:容器display:grid,内部子元素grid-row:1,容器设grid-template-rows: 0fr,点击后改为1fr,子元素overflow:hidden。浏览器会自动追踪内容高度,过渡时长一致。
关键约束是子元素必须能够收缩,因为1fr行不会超过内容但允许压缩。相比max-height大值如500px,grid方案没有因设定值过大而造成的延迟感,且无需JS测量或猜测。
失效场景与降级处理
grid行技巧要求容器是grid元素且子元素需设置min-height:0以覆盖默认的min-height:auto,否则内容最小尺寸会阻止行收缩,导致无法实现0fr过渡。
max-height方法若设定值小于内容高度,内容会被截断;常用做法是设定一个足够大的值,但过渡时长以max-height为基准,例如内容高度100px但max-height200px时,展开到一半时间才达到完整高度,收起时前半段时间内容高度不变(因为max-height仍大于内容),后半段才收缩。
容易答错的地方
- 认为height:auto可以平滑过渡
- 默认无效,因为
auto不是数值。新特性interpolate-size和calc-size()仍处于实验阶段且支持有限,传统方案必须提供确定数值。 - max-height设得越大越保险
- 错误。大值导致过渡时前期速度极快,后期停滞,视觉节奏奇怪。正确做法是尽量接近真实高度,配合JS测量或使用grid方案天然精确。
面试官还会怎么问?
展开时如何避免内容被截断或闪烁?
使用grid时确保子元素min-height为0且overflow为hidden,并只给容器设置transition: grid-template-rows。若用JS测量,需在隐藏时获取scrollHeight,并监听resize重新计算。
收起后display设为none能否优化性能?
可以,在过渡结束后通过transitionend事件将display改为none,避免不可见元素仍占据布局和可访问性树。但直接加display会中断过渡,必须延迟到end且注意取消事件处理。
过渡期间内容高度变化会怎样?
grid方案的行高由内容驱动,若内容高度在过渡期间变化,浏览器会在后续帧重新计算目标值,过渡可能被中断或调整。max-height方案若内容变高,可能超出阈值被裁剪;若变矮,过渡仍继续但视觉上提前结束。JS方案需重设终点值。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。