在网络上,没有什么比页面布局在您尝试查看或与之交互时意外地发生变化或移动更令人恼火的了。无论您是在尝试阅读一篇文章时它都在您面前晃动,还是尝试点击一个链接却发现另一个链接将其推开并带您到意外的地方,这总是令人沮丧的。
这种布局偏移在移动设备上尤其令人沮丧,因为足够大的变化可能会将所有熟悉的内容推离屏幕,导致访问者完全失去方向。我认为,在初始渲染后(无相关用户交互)更改页面布局可能是网站所能产生的最令人不愉快的用户体验。我惊讶地发现,即使是一些大型知名网站,在其他方面做得非常好,也会发生这种情况。好消息是,这是可以修复的。
幸运的是,现代浏览器在样式表加载完成后才会执行初始渲染,这意味着通过一些巧妙的 CSS,我们可以做很多事情来解决这个问题。
是什么导致布局偏移?
最常见的是,它是加载 Ajax 部分的结果。Ajax 是异步的,这意味着没有任何东西在等待它完成。但是,当它完成并且结果被放置到文档中时,它可能会导致重新布局并将其他元素推到周围。
这在广告中尤其常见,但可能发生在任何影响布局的内容上,这些内容在初始渲染后加载,包括图像甚至字体。
我在自己工作的网站上遇到了这个问题
我第一次遇到这个问题实际上不是因为广告或图像,而是因为我们自己的服务器渲染的模板。我当时在 Broadleaf Commerce 工作,在一个现有的管理界面上添加功能。我们不想使用 Angular 之类的东西重建整个界面,但我们确实希望某些内容能够在无需页面刷新即可更新。我们创建了绑定到服务器路由的 Ajax 容器,这些容器将重新渲染并只发送回页面的一部分。它工作得非常好,但在某些情况下,我们有多个这样的部分沿着页面向下排列,中间有静态标题。访问后几秒钟,标题(有时还有其他静态内容)都会向下跳动——正是我们试图避免的那种令人不快的布局偏移体验。
min-height
修复
使用为了修复它,我使用浏览器的 DevTools 测量了结果内容的高度,并将其作为容器的min-height
硬编码到 CSS 中。

min-height: 363px
。这意味着周围的布局开始处于或多或少最终的状态,唯一的变化是内容框的内部。我还为容器设置了背景颜色,以表明某个地方会出现内容。这些简单的添加使界面更加具体和可预测。有人可能会争论是否明智地将最小高度硬编码,因为它使布局与内容分离。这就是为什么我们使用min-height
而不是height
。如果内容最终使元素更高,它仍然可以这样做,这不太好,但比什么都不做要好一点。
这是一个示例,展示了它将是什么样子(重新运行以查看延迟的广告加载)
查看 CodePen 上 CSS-Tricks 团队的笔 避免内容跳动 (@css-tricks)。
另一个示例:第三方小部件
还有一次,我正在为一个客户网站工作,他们希望在他们的结账页面上使用 Braintree 即用型 UI (https://www.braintreepayments.com/products-and-features/drop-in-ui)。

这是一个很棒的小部件,但您会注意到,即使在 Braintree 自己的网站上,当小部件展开时,内容也会被四处推动。值得称赞的是,Braintree 通过平滑过渡来实现这一点,这有助于保持一定的连续性,但我所处理的页面仍然受益于具有min-height
的包装器元素,从而防止结账按钮等基本元素意外移动。
使用过渡修复
使用平滑过渡可以使意外的内容更改不那么令人不快。
.ad-wrapper {
height: 0;
overflow: hidden;
transition: height 0.66s ease-out;
}
.ad-wrapper.loaded {
height: 100px;
}
查看 CodePen 上 CSS-Tricks 团队的笔 避免内容跳动 (@css-tricks)。
缺点是它们仅在您显式设置尺寸时才有效;min-
属性和内容派生的布局更改不会过渡。尽管如此,在您具有不可预测大小的内容并且不介意使用 JavaScript 查询其大小并相应地平滑调整容器大小时,仍然可以使用过渡。
另一个示例:字体加载偏移
在一个不太常见的情况下,我曾经在一个客户网站上工作,该网站突出使用了横向压缩的字体。问题在于它必须加载,因此几秒钟后,几乎所有网站内容都会突然改变大小。它不像上面示例那样剧烈偏移,但由于它影响了页面上的几乎所有内容,因此它更像是一个视觉瑕疵。最后,我找到了这篇非常 棒的文章,它提供了一个冗长但有效的font-family
语句,涵盖了所有主要平台的网络安全压缩字体。但在找到它之前,min-height
再次成为我的朋友。
网络上的示例
The Verge(移动版)
我注意到在 The Verge 上
- 它们顶部的广告有时是小型横幅,有时是大型广告
- 他们似乎已经在为横幅大小的广告使用
min-height
技术
他们的广告 API 可能不会告诉他们广告的大小。如果确实如此,他们应该相应地采用min-height
。如果这不可能,他们可能已经在使用纯 CSS 做了所有他们能做的事情。尽管如此,我仍然认为,从 UX 的角度来看,一个非常大的广告应该放在内容中的更下方,而不是在最顶部,而横幅更适合放在顶部。再说一遍,在用户访问主页时立即向他们显示一个几乎全屏的广告可能是利润丰厚的收入来源。
Kotaku(移动版)

值得注意的是,在 Kotaku 上,主页的最顶部没有广告,因此当您第一次访问它时不会出现跳动。但是,如果页面在已经向下滚动一段距离时加载(例如,在您阅读完文章后按下后退按钮时),则会因帖子流中散布的大型广告而导致一些巨大的、令人迷惑的跳动。它们似乎都是相同的大小,因此这将是一个简单的修复。
总结
在设计任何用户体验时,最重要的是要牢记预期行为。我们期望点击一段蓝色的、带下划线的文本会将我们带到另一个页面。如果它反而打开了一个模态框或什么也没做,那么我们就会遇到一个令人困惑和沮丧的用户体验。我们在当前页面上的导航也是如此。当我们看到一篇文章出现在我们面前时,我们期望它保持静止,除非我们滚动它。当我们看到一个链接或按钮时,我们期望它在我们触碰该位置时仍然存在。违反这些基本假设,轻则令人不快,重则令人沮丧甚至迷失方向。
当我创建了一个使用普通 Moustache 模板的网站时,我很好奇网站在几乎所有内容(甚至主导航)都来自后端时是如何处理这种情况的。
有没有关于如何处理这种情况的好的资源?
我讨厌内容跳动!对我来说,在移动设备上,这是最令人恼火的的用户体验之一。感谢你写这篇文章,希望新开发者能将其作为最佳实践。
你可以使用 https://github.com/changbenny/scrollbear 在图像加载时保持滚动位置。
演示 http://changbenny.github.io/scrollbear/demo/static_img.html
啊,我刚刚在几个网站项目中想到了这一点。很高兴知道我走在了正确的道路上。
非常棒!
我从前端时事通讯上的一个链接来到这里。
具有讽刺意味的是,关闭他们的“弹出窗口”触发了(请注意)完整的页面刷新,从而导致“回到顶部”的内容跳动。
我刚刚看到这篇文章,猜猜我遇到了什么
由于广告占位符未对齐导致的内容跳动!
这是为了强制广告转化而故意为之,还是仅仅“祈求水和饮酒”?
无论如何,网页开发者应该实施另一种策略
选择一个速度慢的电脑/浏览器环境,并使用限速代理服务器。
好建议!Google Chrome 允许你限制选项卡带宽以模拟较慢的连接,这可能是调试此类情况的好方法。
这真的太糟糕了,尤其是当你点击一个链接,然后出现“意外布局偏移”!你点击了你不想点击的东西。哎!
正是这个事件促使我写这篇文章。我希望通过提高人们对这个问题的认识,网络将成为一个对我来说稍微不那么令人恼火的东西;)
硬编码的 min-height 尺寸会造成更多问题。为什么不使用固定纵横比的容器?在这个例子中,无论图像是否加载或页面是否调整大小,图像容器始终具有相同的纵横比: https://radogado.github.io/natuive/#aspect-ratio
News.com 是一个 UI 组件发生偏移的很好的例子。他们有一个新闻流,会在左边弹出。这种情况 100% 的时间都会意外发生,所以对我来说基本上没用,而且非常烦人。
此外,Kijiji 的 Android 应用会不断地在内容顶部加载广告,我已经不小心点击了很多广告,而没有打算查看他们的产品。我可能已经为广告主创造了超过 1000 美元的收入,但他们永远不会看到价值,因为这些都是意外点击。这也非常烦人,现在我知道有一个解决方案,这些问题在未来会变得更加烦人。
在 Chrome 中,你可以启用一个名为“滚动锚定”的标志。当加载新内容时,它会自动移动页面,以便你正在查看的当前部分保持在视野中。
转到 chrome://flags/#enable-scroll-anchoring
你也可以使用固有比率为图像或其他组件创建占位符。这通常优于设置 min-height,因为它是一种响应式解决方案。
这篇文章很好地解释了这种技术: http://alistapart.com/article/creating-intrinsic-ratios-for-video
这是我对用户体验和网页开发的绝对第一大烦心事。正如文章所述,我们作为开发者需要预料到几乎没有任何内容会填充页面的布局,因为几乎所有内容都是异步加载的。
Radoslav 在上面简要提到了这一点,我认为比预测第三方小部件等更重要的是预测图像,方法是强制使用纵横比。这很容易通过使用像
<figure />
这样的<img />
包装器来解决,该包装器通过基于百分比的 padding-top/bottom 策略设置了固定的纵横比。当然,客户端代码需要在页面加载时预先知道图像的尺寸,但大多数 Web 服务器都有提供此信息的方法。