必须显式设置内联width/height属性,因为浏览器解析html时css尚未生效,0×0占位导致图片加载后重排并引发cls;仅css宽高无效,aspect-ratio仅为补充且兼容性有限。

为什么只写 CSS 宽高对 CLS 没用
浏览器解析 HTML 时,<img> 或 <iframe></iframe> 若没带内联 width 和 height 属性,会按 0×0 占位——CSS 设置的宽高此时还没生效,渲染树已按空盒布局。等图片加载完成、CSS 计算完毕,再重排,下方内容就被顶下去,直接贡献 CLS。
常见错误写法:<img src="a.jpg" style="max-width:90%"> 或 <img src="a.jpg" class="responsive-img">(CSS 里设宽高)——这些统统不阻止初始占位抖动。
- 必须显式写
width="600" height="400",哪怕响应式场景也要保留 -
aspect-ratio: 16/9是好补充,但不能替代 HTML 属性;Safari 15.4+、Firefox 89+ 才稳定支持,旧版本 fallback 仍靠属性 -
srcset+sizes时更要配width/height,否则浏览器可能先按默认尺寸(如 300×150)占位,再根据sizes调整,造成两次偏移
响应式图片怎么安全设宽高
固定像素值在移动端会拉伸或溢出,但去掉又触发 CLS——折中方案是用“最小安全宽高 + aspect-ratio”组合。
正确写法示例:<img src="hero.jpg" srcset="hero-sm.jpg 480w, hero-lg.jpg 1200w" sizes="(max-width: 768px) 100vw, 800px" style="max-width:90%" style="max-width:90%" alt="Hero">
-
width/height填最大可能尺寸(如设计稿最大宽度),让浏览器预留足够空间 - CSS 中用
width: 100%; height: auto; aspect-ratio: 2/1;控制实际渲染比例 - 不要用
max-width: 100%替代width属性——它不提供初始占位信息 -
decoding="async"可降低解码阻塞,但 Safari 不支持,且不解决占位问题,仅作辅助
iframe 和视频的宽高声明陷阱
<iframe></iframe> 和 <video></video> 同样依赖内联 width/height,但容易被 loading="lazy" 或第三方嵌入脚本绕过。
- YouTube 嵌入必须带
width="560" height="315",哪怕你用 JS 动态插入也要提前写死属性 -
<iframe src="..." loading="lazy"></iframe>不能省略宽高——loading只控制加载时机,不提供尺寸预期 - 第三方广告脚本常注入无尺寸
<div>,检查其 DOM 插入前是否已有 <code>min-height占位容器,比如:<div id="ad-slot" style="min-height: 250px"></div> - 避免用
visibility: hidden或opacity: 0占位——它们不脱离文档流,也不提供高度,等于没占 - 必须用
@font-face配font-display: swap,禁用block(阻塞渲染)和optional(可能跳过加载) - 加
size-adjust: 102%或descent-override: 25%对齐字体度量,防止替换时高度跳变 - 关键文本容器预设
line-height和min-height,例如:<h1 style="line-height: 1.4; min-height: 1.4em"></h1> - 别在
font-family列表里混用等宽与比例字体(如"Monaco", "Helvetica"),ch/ex单位行为不一致,宽度易跳
字体加载引发的文本 CLS 怎么压住
自定义字体加载期间用系统字体撑开行高,Web Font 加载后重绘,若两套字体度量值差异大,<h1></h1> 或段落高度突变,下方内容就被拽下去。
CLS 不是 HTML 能“生成”的东西,它只是浏览器对布局失控的计分结果。真正起作用的,是你在 HTML 源码里写的那几个 width、height、style="min-height",以及 @font-face 里的 size-adjust——这些静态声明,才是浏览器能最早读到、最可靠执行的占位指令。漏掉任意一个,都可能在用户滚动到那里时,突然抖一下。











