不能直接对用position: absolute做占位,因其脱离文档流致父容器塌陷、无法推导宽高,必须用position: relative的包裹容器显式设尺寸并内部绝对定位图片。

为什么不能直接对 <img> 用 position: absolute 做占位
常见错误是给 <img> 自身加 position: absolute,再配 top: 50%; left: 50%; transform: translate(-50%, -50%) 居中。这会导致父容器高度塌陷为 0,骨架或背景根本撑不开空间——你看到的“占位”其实是父级空白,不是真实预留。
根本原因:<img> 是 inline 元素,position: absolute 后完全脱离文档流;浏览器无法从它推导出宽高,aspect-ratio 和 padding-top 都失效。
- 必须用一个包裹
<div class="img-placeholder"></div>,且该<div> 显式设 <code>position: relative - 这个
<div> 要有明确尺寸:要么写死 <code>height: 200px,要么用aspect-ratio: 16 / 9(Safari ≤15.3 需 fallback 到padding-top: 56.25%) -
<img>放在内部,设position: absolute; top: 0; left: 0; width: 100%; height: 100%,再加object-fit: cover - 骨架用
::before伪元素实现时,它的border-radius、box-shadow、border-width必须和真实图片样式完全相同 - 所有骨架容器必须加
overflow: hidden,否则linear-gradient扫光动画会滑出圆角,露出底色 - 如果图片用了
object-position: center top,骨架也要模拟相同裁剪偏移,否则视觉中心错位 - 老 Safari(iOS 15.0–15.3)对
padding-top计算不稳定,建议在容器上加min-height: 0.01px或line-height: 0防忽略 - 正确做法:占位图(如
::before)和<img>都保持position: absolute,初始状态都设z-index: 1 - 图片加载完成时,只改
<img>的opacity(从 0 → 1),并加transition: opacity 0.3s ease - 同时在
img.is-loaded::before里写display: none,而不是靠z-index压它下去 - 千万别用
visibility: hidden → visible:不触发动画,且可能引发重排 - 必须监听那个
position: relative的包裹容器(如.img-placeholder) - 初始化
IntersectionObserver前,确认容器已渲染且有尺寸——可在requestAnimationFrame或setTimeout(..., 0)中检查offsetHeight - 加防抖:滚动中快速进出视口时,用
setTimeout延迟 50ms 再加载,避免重复请求 - 加载失败后,记得 fallback 到
src或显式显示错误占位图,别留白
如何让骨架屏和真实图片尺寸严丝合缝不跳动
布局抖动(CLS)几乎全因占位容器和最终图片渲染尺寸差了哪怕 1px。不是“差不多就行”,而是必须一致。
z-index 在懒加载切换中该怎么用才不闪屏
很多人想用 z-index 控制“占位图在下、真实图在上”,但直接切 z-index 不触发重绘,反而容易卡顿或叠错层。
IntersectionObserver 应该监听谁?为什么监听 <img> 常常失效
原生 loading="lazy" 在 position: absolute 的 <img> 上大概率不工作,尤其当父容器高度为 0 或刚插入 DOM 时。浏览器根本判断不出它是否进入视口。











