图片/iframe未设width和height属性必然触发CLS:浏览器按0×0占位,加载后重排致内容下移;必须同时设置原始像素值(如width="1200" height="800"),CSS宽高无效,aspect-ratio需现代浏览器支持。

图片/iframe没写width和height属性就加载,必然触发CLS
浏览器解析HTML时,遇到没有width和height的<img>或<iframe>,会按0×0占位;等资源加载完成再重排,下方所有内容直接下移——这是最常见、最“稳”的CLS来源。
必须同时写两个属性,且值为原始像素数字(如width="1200" height="800"),不带单位、不写百分比、不写auto。仅靠CSS设置宽高无效,loading="lazy"也不能替代。
- 响应式场景可用
style="aspect-ratio: 16/9; width: 100%; height: auto;",但Safari 15.4+、Firefox 89+才稳定支持 -
srcset+sizes仍要配width/height,否则浏览器可能先按默认尺寸占位,再调整,造成两次偏移 - 第三方嵌入(如YouTube iframe)若没尺寸,哪怕加了
loading="lazy",也照跳不误
动态插入内容没预留容器高度,<body>就成了CLS背锅侠
Lighthouse报<body>贡献CLS,不是<body>本身有问题,而是它内部JS插入的广告、评论、推荐模块没占位——这些元素一出现,就把原有内容往下顶,整个页面“沉”一下,<body>自然被记为源头。
不能用visibility: hidden或opacity: 0占位,它们不脱离文档流但无尺寸,等于没占。也不能等JS加载完再appendChild,那已经晚了。
立即学习“前端免费学习笔记(深入)”;
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 在HTML源码里就写好占位:
<div id="ad-slot" style="min-height: 250px"></div>,高度尽量接近历史均值 - 必须是
min-height或固定height,height: 0; overflow: hidden初始状态 + JS加载后展开,也算一种保守策略 - 若必须用JS控制位置,优先用
transform: translateY()或position: absolute,让元素脱离文档流,不参与布局挤压
font-display: swap不用size-adjust配合,字体加载照样引发CLS
用font-display: swap能让文本立即显示系统字体,等自定义字体加载完再换——听起来很美,但如果两套字体行高、字宽差异大,切换瞬间文本块高度突变,下面内容就被顶下去,CLS照算。
font-display: optional更危险:它可能跳过加载,导致字体反复切换,CLS反而更高。
- 必须在
@font-face里加size-adjust: 102%(数值根据字体实际度量微调)或ascent-override/descent-override - 关键容器(如
<h1>、按钮)建议预设line-height和min-height,防止高度塌缩或撑开 - 避免在
font-family列表里混用等宽与比例字体(如"Monaco", "Helvetica"),ch/ex单位行为不一致,易引发宽度跳变
SPA路由切换或水合后内容渲染,CLS容易漏检
单页应用中,Lighthouse跑一次只测首屏,但用户点个Tab、切个路由,新内容渲染引发的偏移可能被归到前一个页面的CLS总和里——你优化了首页,却没发现二级页在“跳”。
用getCLS({ reportAllChanges: true })上报每次偏移片段,比只看最终值有用得多;attribution能定位具体元素,getLayoutShifts()可捕获单次位移细节。
- 手动监听
navigation事件,在路由变化后调用reset()重新开始计算 - Chrome DevTools → Rendering面板开启
Layout Shift Regions,实时观察哪些区域在跳 - 检查元素computed styles,确认是否因
position: absolute脱离文档流后又被JS修改top/left,导致父容器未预留空间


















