CSS 折叠引发的 scrollHeight 异常 —— 一次 Blink 引擎的诡异 Bug
现象描述
外层是一个滚动容器,用于渲染消息列表。
列表中有一个答案消息组件,它分为两部分:
- 思考步骤(可折叠)
- 内容
思考步骤部分支持折叠。在折叠状态下,会给它加上 CSS:
.thinking-content-collapse {
max-height: 0;
opacity: 0;
overflow: hidden;
}
.thinking-content {
transition: all .3s ease;
}
之所以不使用
display: none,是因为需要在展开 / 折叠时呈现不透明度的过渡动画。
在普通情况下,一切正常:
- 折叠 / 展开能正常触发
- 滚动容器的
scrollHeight会根据答案消息组件的高度动态变化
但是!
当渲染一个超长的答案消息组件(思考步骤高度约 18000px,内容高度约 2000px)时,在折叠状态下,滚动容器底部会出现一大片空白,而且此时滚动容器的 scrollHeight 诡异地变成了约 12000(不确定这个数字是怎么来的)。
排查过程与原因
排查了很久,最终在 Claude Code 的帮助下,得到解释:
这是 Blink 引擎(Chrome / Edge)的一个边界 Bug:
当元素同时满足max-height: 0+overflow: hidden+opacity: 0时,scrollHeight的计算路径没有正确应用max-height约束,导致内部内容高度“泄漏”到滚动容器的scrollHeight中。
解决方案
Claude 给出两种可行方案,实测都能解决问题:
-
给
.thinking-content增加contain: layout
作用:隔离元素内外的布局计算。
缺点:存在浏览器版本兼容性问题。 -
给
.thinking-content-collapse增加transform: scaleY(0)
作用:强制创建合成层,使元素走独立的布局计算路径。 缺点:需要对应修改下transition动画的值,否则transform也会产生动画
我们最终采用了方案 2 来修复问题。
疑问
虽然问题解决了,但底层触发机制和原理仍不太清楚。
如果有大佬了解其中的原理,欢迎留言交流,非常感谢!