先记住这个答案
height: auto 的意思是「高度由内容决定」,但这里的内容只包括留在文档流里的子元素。position: absolute 的元素被移出了文档流,它只把包含块当作定位的坐标系,不参与父元素的高度计算,所以父元素只有 padding 和其它普通子元素的高度,没有普通子元素时直接塌成 0。解决思路有三个:让子元素回到文档流,用 Grid 把要叠放的元素放进同一个格子,或者用 JS 测出子元素高度再写给父元素。
height: auto只统计文档流内的子元素absolute脱离文档流,父元素高度会塌成 0- 要叠放又要撑高,用 Grid 把子元素放进同一格
- 未知高度可用
ResizeObserver观察
先看父元素塌成 0 的样子
最常见的场景是卡片上叠一层文字:图片和文字都想定位在卡片里,于是全写成了 absolute,结果卡片自己没有高度,后面的内容顶上来和它重叠。
absolute 的卡片CSS.card {
position: relative; /* 只是给子元素当定位参照 */
/* height 默认就是 auto */
border: 1px solid #ccc;
}
.card .caption {
position: absolute; /* 脱离文档流 */
top: 0;
left: 0;
padding: 16px;
}
/* 结果:.card 的内容高度是 0,只剩上下两条边框挨在一起,
.caption 溢出到卡片外面,压在后面的元素上 */.card 计算高度时会遍历流内子元素,.caption 不在其中,所以它的 auto 高度等于 0。.caption 仍然会画出来,只是画在了父元素的盒子外面。
很多人会先去改父元素的 position,但 relative、absolute、fixed 都只影响「子元素相对谁定位」,不改变「子元素是否参与父元素尺寸」。
- 和浮动塌陷不是一回事
- 浮动元素也会让父元素高度塌陷,但浮动只是半脱离文档流:给父元素加
display: flow-root创建 BFC,浮动元素就会被算进高度。绝对定位元素是完全脱离,创建 BFC 对它没有作用,overflow: hidden也只会把它裁掉,不会把父元素撑高。
让父元素跟着内容长高的三种办法
先问一句:这个子元素真的需要 absolute 吗?很多时候用 absolute 只是为了叠放或者偏移,这两个需求都有不脱离文档流的写法。
.card {
display: grid;
}
.card > * {
grid-area: 1 / 1; /* 所有子元素都放进第一行第一列 */
}
.card .caption {
align-self: end; /* 在格子里靠底部对齐,代替 bottom: 0 */
padding: 16px;
}
/* 结果:图片和文字叠在一起,格子高度取两者中较高的那个,
.card 被正常撑开,后面的内容不会再压上来 */这是现在最推荐的叠放写法。子元素都还在文档流里,Grid 的行高按内容最高的那个来算,父元素自然就被撑开了。需要控制上下层时,给子元素设 z-index 就行,Grid 子项不写 position 也能用 z-index。
- 只想偏移一点,用
position: relative - 如果只是想把元素往某个方向挪几像素,用
position: relative加top、left。它在原来的位置上保留占位,父元素高度照常计算,只是画的时候偏了一下。也可以用transform: translate(),效果类似,而且不触发重新布局。 - 真要
absolute时用 JS 同步高度 - 有的场景确实需要
absolute,比如轮播图里几张幻灯片叠在一起、切换时淡入淡出,父元素要跟当前那张一样高。这时用ResizeObserver监听当前幻灯片,把它的offsetHeight写到父元素的height上。能用 Grid 叠放解决的就别走这条路,它多了一次测量,首帧也可能闪一下。
容易答错的地方
- 以为给父元素加
position: relative就能包住 - 这是最常见的误解。父元素加
relative后,子元素的top、left以父元素为参照,看起来「在父元素里」,但父元素高度一点没变。定位参照和尺寸计算是两件事。 - 以为清除浮动的办法也能用在这里
clearfix、overflow: hidden、display: flow-root都是让父元素把浮动元素算进高度,对absolute元素完全无效。回答时把两种「脱离文档流」分开说,面试官会觉得你是真懂。- 给父元素写死一个
height了事 - 写死高度只在内容长度固定时成立。文案一变长、字号一放大、换成窄屏折行,子元素就溢出了。实在要兜底,至少用
min-height,并且接受内容更多时仍可能溢出。
面试官还会怎么问?
absolute 元素的包含块是谁?
是离它最近的、position 不是 static 的祖先元素的 padding 盒。找不到这样的祖先时,包含块是初始包含块,也就是视口大小的那个矩形。另外,祖先如果设置了 transform、filter 或 perspective,也会成为 absolute 和 fixed 元素的包含块。
position: sticky 会不会撑开父元素?
会。sticky 元素没有脱离文档流,它先按普通流布局占好位置,滚动时才在父元素范围内粘住。所以它参与父元素高度,而且它粘住的范围也被父元素的边界限制住。
父元素是 Flex 容器,absolute 子元素还参与布局吗?
不参与。Flex 和 Grid 容器里的 absolute 子元素同样脱离流,不占 flex 项或网格格子的位置,也不影响容器尺寸。区别是它的静态位置会按容器的对齐方式来算,所以不写 top、left 时,它的初始落点可能受 justify-content 和 align-items 影响。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。