必须清除浮动才能让父容器正确计算高度,推荐用display: flow-root创建BFC;读取scrollHeight需在requestAnimationFrame中执行,并避免浮动与max-height动画混用。

浮动元素本身不参与高度计算,别指望它“自动撑开”菜单容器
浮动(float)的设计初衷是图文环绕,不是布局容器。一旦子元素设了 float: left 或 float: right,它们就脱离常规文档流,父容器会高度塌陷——即使菜单已展开,.nav-menu 的 height 和 scrollHeight 仍可能为 0 或远小于真实内容高度。
这不是 bug,是浮动机制的必然结果。你看到的“菜单展开”,往往只是视觉呈现,DOM 结构里父容器根本不知道里面有什么。
- 用
getComputedStyle(menu).height或menu.offsetHeight读到的几乎总是 0(尤其初始display: none时) -
scrollHeight也靠不住:浮动子项不贡献块级高度,浏览器无法汇总出真实内容高度 - 媒体查询或
:checked切换后,浮动布局重排不会触发resize或transitionend,JS 无从监听“何时算完”
必须清除浮动,才能让父容器感知真实高度
想让 scrollHeight 可读、让 max-height 动画有依据,第一步是让父容器重新建立包含关系。最直接有效的做法是给菜单容器加 overflow: hidden 或 display: flow-root(推荐后者,语义清晰且无副作用)。
-
display: flow-root是现代标准解法,强制创建新的 BFC,天然包含所有浮动子元素,且不影响布局行为 -
overflow: hidden能触发 BFC,但会意外裁剪position: absolute的下拉菜单,需额外处理 - 避免用
clear: both的伪元素方案(如::after { content: ""; display: table; clear: both; }),它只解决塌陷,不保证scrollHeight准确——尤其当浮动项含文字换行、图标或 padding 时
清除浮动后,JS 读取 scrollHeight 的时机仍很关键
即便父容器已能“看见”内容,scrollHeight 仍可能读小:字体未加载完成、图片未解码、Web Font 触发 FOIT/FOUT,都会导致首次测量值偏小。不能在 DOMContentLoaded 立即执行。
- 用
requestAnimationFrame套一层是最小代价的等待:确保样式已应用、布局已计算 - 如果菜单初始为
display: none,需临时设为visibility: hidden; position: absolute; display: block再读,否则scrollHeight === 0 - 动态增删菜单项后,必须手动触发重读——不要依赖 resize 监听,高度基本不变,只在 DOM 变更后才需更新
别把 max-height 动画和浮动混在一起做
浮动 + max-height 过渡看似可行,实则隐患密集:Safari 对 calc(var(--h) * 1) 类写法动画支持不稳定;iOS 上浮动项重排可能触发异步 layout,导致过渡跳帧;更麻烦的是,overflow: hidden 容器内浮动元素若超出边界,会被静默裁剪,用户看不到“被截断”的提示。
- 真正稳妥的做法:放弃浮动布局,改用
display: flex或grid——它们天然支持内容高度反馈,scrollHeight准确,max-height动画稳定 - 若必须保留浮动(如兼容老项目),至少把菜单容器设为
display: flow-root,再用 JS 注入--menu-height,CSS 中写max-height: var(--menu-height, 300px),fallback 值略大于预估最大高度 - 永远配
overflow: hidden,否则过渡中浮动项可能溢出容器边缘,造成视觉撕裂
浮动布局在响应式菜单中早已过时。它带来的高度不可预测性,远超其“轻量”的表象优势。真正难的不是怎么读高度,而是意识到:只要还在用 float 控制菜单结构,你就已经站在了问题源头。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











