不知道你有没有经历过这样的场景:当你打开一张“多图杀猫”的页面后,正一张图一张图边滚边看,在你刚准备定睛看某一张图的时候,这张图突然被它上面的内容挤到了视口下方,然后你赶紧把滚动条往下拉,试图追赶这张没看完的图,当你刚刚追上的时候,这张图又一次被挤到了你看不见的地方。

发生这种情况的原因是因为在很多场景下(比如论坛里),你没法事先知道一张图的高度,所以你没法事先给这张图占位,在网速不理想的情况下,可能就会发生我上面描述的这种因页面靠上的图片比靠下的图片晚加载出来而导致用户当前浏览的内容被频繁挤出视口的情况。

我通过在定时器回调里向页面上方插入图片来模拟一下刚才描述的这种情况:

<style>
img {
display: block;
margin: 0 auto;
}
</style>
<img src="https://aecpm.alicdn.com/tfscom/TB1.52aPFXXXXa0XXXXXXXXXXXX.jpg">
<img src="https://aecpm.alicdn.com/tfscom/TB1_utRPVXXXXapXVXXXXXXXXXX.png">
<img src="https://static.dingtalk.com/media/lAHOuOFd_czSzQEn_295_210.gif">
<img src="https://aecpm.alicdn.com/tfscom/TB1f1xwQpXXXXXBXVXXXXXXXXXX.jpg">
<img src="https://gtms03.alicdn.com/tps/i3/TB1eSxvJVXXXXaKXFXXYoAvIXXX-220-50.png">
<img src="https://gw.alicdn.com/bao/uploaded/TB1EGvvPVXXXXX3aXXXXXXXXXXX-200-200.jpg">
<img src="https://gw.alicdn.com/tfscom/TB1CLTHNFXXXXaDXpXXXXXXXXXX">
<script>
const urls = `
https://asearch.alicdn.com/bao/uploaded/i1/1381306006414474986/TB2_gZAlNtmpuFjSZFqXXbHFpXa_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i1/153360285303496277/TB2SO.Wa4vzQeBjSZFEXXbYEpXa_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i1/188050339412916381/TB2geTXaypnpuFjSZFkXXc4ZpXa_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i2/181720289489216985/TB2UFz6amjz11Bjy0FnXXcnxXXa_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i3/108480250457898935/TB28r5osFXXXXbrXXXXXXXXXXXX_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i3/111180208599309441/TB2kAsQnVXXXXXcXFXXXXXXXXXX_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i3/171530328819399773/TB2rgtke9iK.eBjSZFsXXbxZpXa_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i3/1880505035634435666/TB2bToNiHXlpuFjSszfXXcSGXXa_!!0-saturn_solar.jpg
https://asearch.alicdn.com/bao/uploaded/i4/1519305020726924733/TB2I2VuhNhmpuFjSZFyXXcLdFXa_!!0-saturn_solar.jpg
`.split("\n")
let i = 0
setInterval(() => {
if (i === urls.length) i = 0
let img = new Image()
img.src = urls[i++]
document.body.prepend(img)
}, 2000)
onscroll = function(argument) {
console.log("scrollY:" + scrollY)
}
</script>

上面这个 demo 里,假设我一直“追赶”的那张图是“金凯瑞摇头三人组”那张 GIF,那么在 Chrome 56 之前的版本以及在其它的浏览器中,你看到的会是下面这样的场景:

为了获得更好的用户体验,Chrome 从 56 开始,开启了一个叫做“滚动锚定(Scroll Anchoring)”的优化,效果就是,当页面在视口上方的部分突然变高了 x 像素,那么浏览器会为你自动向下滚动 x 像素,从而保证视口内容完全不变:

浏览器自动为你向下滚动 x 像素,就意味着浏览器自己会触发一次 scroll 事件,也意味着 scrollY 的值会增加 x,你可以通过上面的 demo 验证这一点。

有些同学可能会有疑问,“这种场景多吗?”、“我怎么从来没注意到?”、“有必要把事情搞复杂吗?”。 从 Chrome 官方的统计可以看到,这个特性被触发(替你滚动页面)的概率大概为 1%,并不多,但也算不上是极端情况,所以优化还是有必要的。可能因为近些年网络条件越来越好,图片加载的速度比你滚动页面的速度还要快,所以不太容易遇到因网速慢导致的这类场景了(尤其在 WIFI 网络下)。

不过这个优化的确不是个简单的改动,Chrome 从去年 3 月份开始实现这个特性,直到一年多后的今天,仍然有一些因这个优化导致的 bug 存在,这些 bug 多表现为页面异常滚动,甚至像永动机一样无限抖动,从这方面看,事情的确有一些被搞的复杂了。但幸好有一个 CSS 属性可以关掉这个优化:overflow-anchor: none,你可以把这个属性添加到发生 bug 的容器元素上,甚至加到 body 元素上也行,然后该元素及其它的所有后代节点就都不会被应用“滚动锚定”的优化了。除了作为浏览器 bug 的临时 fix,我想不到其它使用这个属性的场景了。

这个优化不仅限于看图片的时候,任何元素节点,甚至文本节点也同样适用。比如你在某新闻网站浏览一段文字的时候,视口上方突然异步插入了一个未事先占位的 iframe 广告(微博输入框下方就有这么一个广告),如果你使用了 Chrome 56 及以上版本的话,你完全察觉不到这一变化,你的阅读不会被打断。

页面在视口上方的高度增加 x 像素,浏览器会为你向下滚动 x 像素;反过来,页面在视口上方的高度减少 x 像素,浏览器也会为你向上滚动 x 像素,但这种情况更少见了。

该优化同样适用于元素级别的滚动条,我也写了一个 demo:

<style>
div {
width: 300px;
height: 300px;
} #container {
background: red;
overflow: scroll;
} #aboveViewport {
background: blue;
} #anchorNode {
background: green;
}
</style>
<div id="container">
向下滚动到底
<div id="aboveAnchorNode"></div>
<div id="anchorNode"></div>
这段文字一旦出现就会始终在视口内
</div>
<script>
let height = 100
setInterval(() => {
aboveAnchorNode.style.height = height += 10
}, 1000)
</script>

由于本文讲的是一个浏览器的优化,即便是前端开发者也没有深究的必要,所以我故意省略了一些内容,比如什么是锚定节点(anchor node )以及浏览器如何选定一个锚点节点?以及哪些样式改动会把锚定节点挤出视口但不会触发优化(Suppression Triggers),如果你想深究,可以从规范里找到答案。

滚动锚定(Scroll Anchoring)- 让视口内容不再因视口上方 DOM 元素的高度变化而产生跳动的更多相关文章

  1. dom元素的自上而下自动滚动

    dom元素的自上而下自动滚动 <!doctype html> <html lang="en"> <head> <meta charset= ...

  2. 解决:mui 的 选项卡 + 下拉刷新 功能,在其中嵌入 iframe 后,在 iphone 的情况下,iframe 的内容不能滚动,只显示第一屏内容。

    我所遇到的情况是,使用 mui 的 选项卡 + 下拉刷新 功能时,其中有2个页面是嵌入了别的网站的页面,而别个几个是通过 ajax 加载本网站的数据.然后 在其中嵌入 iframe 后,在 iphon ...

  3. [转]用 jQuery 实现页面滚动(Scroll)效果的完美方法

    转自: http://zww.me/archives/25144 很多博主都写过/转载过用 jQuery 实现页面滚动(Scroll)效果的方法,但目前搜来的方法大都在 Opera 下有个小 Bug: ...

  4. 滚动锁定 scroll lock 键有什么用?

    滚动锁定 scroll lock 键有什么用? 中文名称:滚动锁定键  按下此键后在Excel等按上.下键滚动时,会锁定光标而滚动页面:如果放开此键,则按上.下键时会滚动光标而不滚动页面.      ...

  5. 取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏

    取消设置透明状态栏,使 ContentView 内容不再覆盖状态栏,在MainActivity中添加以下代码: getWindow().clearFlags(WindowManager.LayoutP ...

  6. JS复制DOM元素文字内容

    要实现的效果:将HTML页面中的某个DOM元素例如DIV下面的文本内容进行复制. 实现过程如下: <html> <head> <title>Copy text De ...

  7. 第一百六十六节,jQuery,基础 DOM 和 CSS 操作,元素内容,元素属性,css和class,元素宽度高度、偏移、滚动条

    jQuery,基础 DOM 和 CSS 操作,元素内容,元素属性,css和class,元素宽度高度.偏移.滚动条 学习要点: 1.DOM 简介 2.设置元素及内容 3.元素属性操作 4.元素样式操作 ...

  8. Android中竖线随内容高度变化而变化的问题和解决办法

    项目中要求显示竖线,并且竖线高度不确定,竖线的高度要随着内容的变化而变化.不能使用match_parent 充满,也不能在布局中写死,此时使用 android:layout_height=" ...

  9. 如何根据搜索页面内容得到的结果生成该元素的xpath路径

    如何根据搜索页面内容得到的结果生成该元素的xpath路径?

随机推荐

  1. QQ的孤独

    接触网络到现在,已是好多年前的事了.初识网络犹如初恋般,充满了新鲜和好奇,于是,从聊天室到QQ,MSN,邮箱,再去BBS.然后有了博客^^^^^^ 那时的网络于我是那般充满诱惑,整天穿梭其中,乐此不彼 ...

  2. addq

    <template> <el-row id="AddRoom"> <el-col :xs="0" :sm="2" ...

  3. 如何在查看docker container内进程信息,与宿主机上进程信息的映射关系

    docker container内运行的进程,在宿主机上,通过ps也是能够查到的,但是在不熟悉命令的时候,无法快速找到他们的关系. 这里科普一个基础命令 docker top 1. 找到容器的id d ...

  4. Java的动态代理

    什么是动态代理(dynamic proxy) 动态代理(以下称代理),利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的新类(也称“动态代理类”)及其实例(对 ...

  5. R语言学习——向量

    以下为在RStudio中输入 #为注释符,其后内容程序不执行 > #向量是用于储存数值型.字符型或者逻辑型数据的一维数组.执行组合功能的函数c()可用来创建向量.示例如下: > a< ...

  6. 8-过滤器Filter和监听器Listener

    一.web监听器:监听特殊事件的发生1.监听实现步骤 a.写一个java类,实现特定的接口,重写相关方法 b.在web.xml中,牌配置 <listener> <listener-c ...

  7. cumprod、prod函数

    1.prod函数 prod函数用于求矩阵元素的积,其调用格式如下. (1)B=prod(A):若A为向量,则返回所有元素的积:若A为矩阵,则返回各列所有元素的积. (2)B=prod(A,dim):返 ...

  8. 【Topcoder 10689】TheSoccerDivOne

    题意:给\(n\)个队伍的积分,它们要踢足球,每个队伍剩下4场没有踢. 问踢完后\(0\)队伍最高排名. 思路:首先想了贪心,可惜不对. 那么老实dp. 首先:每个队伍具体和哪个人踢了没有关系. 那么 ...

  9. 领域驱动设计系列文章(2)——浅析VO、DTO、DO、PO的概念、区别和用处

    本篇文章主要讨论一下我们经常会用到的一些对象:VO.DTO.DO和PO. 由于不同的项目和开发人员有不同的命名习惯,这里我首先对上述的概念进行一个简单描述,名字只是个标识,我们重点关注其概念: 概念: ...

  10. 区块链代币(Token)笔记 — — 术语

    前言 接触区块链和数字货币差不多有大半年时间,一直在赶项目进度,现在有空整理补习一下相关的知识,只谈代币不谈区块链