使用IntersectionObserver更高效的监视某个页面元素是否进入了可见窗口

比如说,你想跟踪 DOM 树里的一个元素,当它进入可见窗口时得到通知。 也许想实现即时延迟加载图片功能,或者你需要知道用户是否真的在看一个广告 banner。 你可以通过绑定 scroll 事件或者用一个周期性的定时器,然后再回调函数中调用元素的 getBoundingClientRect() 获取元素位置实现这个功能。 但是,这种实现方式性能极差,因为每次调用 getBoundingClientRect() 都会强制浏览器重新计算整个页面的布局,可能给你的网站造成相当大的闪烁。 如果你的站点被加载到一个 iframe 里,而你想要知道用户什么时候能看到某个元素,这几乎是不可能的。 单原模型(Single Origin Model)和浏览器不会让你获取 iframe 里的任何数据。 这对于经常在 iframe 里加载的广告页面来说是一个很常见的问题。

IntersectionObserver 就是为此而生的,它让检测一个元素是否可见更加高效,而且已经在 Chrome 51 中实现。 IntersectionObserver 能让你知道一个被观测的元素什么时候进入或离开浏览器的可见窗口。

如何创建一个 IntersectionObserver

API 比较简单,最好用一个例子说明:

var io = new IntersectionObserver(
    entries => {
        console.log(entries);
    },
    {
        /* 使用默认参数。下面详细说明 */
    }
);
// 开始观测某个元素
io.observe(element);

// 停止关注某个元素
// io.unobserve(element);

// 禁用整个 IntersectionObserver
// io.disconnect();

  

使用 IntersectionObserver 的默认属性,当元素部分进入可见窗口或完全离开可见窗口时都会调用你的回调函数。

如果你需要观测多个元素,你可以用——而且是推荐使用——同一个IntersectionObserver 实例调用多次 observe()

一个 entries 参数会被传递给你的回调函数,它是一个IntersectionObserverEntry 对象数组。 每个对象都包含更新过的交点数据针对你所观测的元素之一。

[IntersectionObserverEntry]
    time: 3893.92
  ??rootBounds: ClientRect
      bottom: 920
      height: 1024
      left: 0
      right: 1024
      top: 0
      width: 920
  ??boundingClientRect: ClientRect
    // ...
  ??intersectionRect: ClientRect
    // ...
    intersectionRatio: 0.54
  ??target: div#observee
    // ...

 rootBounds 是在根元素上调用 getBoundingClientRect() 的结果,默认就是可见窗口。boundingClientRect 是在被观测元素上调用 getBoundingClientRect的结果。intersectionRect 是这两个矩形的交界,它告诉你被观测的哪块区域是可见的。intersectionRatio 把两个紧紧关联起来,告诉你元素有多少可见。 有这些信息供你使用,你就能非常高效地实现一些像即时加载资源等功能。

IntersectionObserver 总是异步传输这些数据,你的回调函数代码会在主线程运行。
另外,规范明确指出 IntersectionObserver 实现应该使用requestIdleCallback()。 这意味着调用你注册的回调函数的优先级较低,浏览器在空闲时间才会这样做。 这是一个有意识的设计决定。

滚动 div

我不是很喜欢在某个元素里滚动,但我不会再这里和你争,IntersectionObserver 也不是。 当你不是在滚动整个可见窗口时,options 对象可带一个 root 参数用于指定滚动的元素。 有一点很重要你需要牢记:root需要是所有被观测元素的直接或间接父级。

检测所有东西!

不能这样!作为一个开发者写出这样的代码是不称职的。 这种用法对用户的 CPU 非常不友好。 考虑一个无线滚动的例子,在这种情况下,更为可取的方案是在 DOM 添加岗哨,观测(并且复用)他们。 你应该在无线滚动区域的最后一个元素之后添加一个岗哨。 当那个岗哨进入可见窗口时,你就可以在回调函数里加载数据,创建后面的元素,添加到 DOM 里,并且随之更新岗哨的位置。 如果你正确的复用了这些岗哨,就无需在调用 observe()。 IntersectionObserver 扔可以继续工作。

Moar Updates, Please

如同之前所说,当被观测的元素部分进入可见窗口时会触发回调函数一次,当它离开可见窗口时会触发另一次。 这样就回答了一个问题:元素 X 在不在可见窗口里。 但在某些场合,仅仅如此还不够。

就就轮到 threshold 起作用了。 它允许你定义一个 intersectionRatio 临界值。 每次 intersectionRatio 经过这些值的时候,你的回调函数都会被调用。threshold 的默认值是[0],就是默认行为。 如果我们把 threshold 改为 [0, 0.25, 0.5, 0.75, 1],当元素的每四分之一变为可见时,我们都会收到通知:

还有其他别的属性吗?

到目前为止,仅剩一个属性没在上文列出。 rootMargin 允许你指定到跟元素的距离,允许你有效的扩大或缩小交叉区域面积。 这些 margin 使用 CSS 风格的字符串,例如 10px 20px 30px 40px,依次指定上、右、下、左边距。 总结一下,IntersectionObservers 结构提供了如下选项:

new IntersectionObserver(entries => {/* … */}, {
  // 用于计算相交区域的根元素
  // 如果未提供,使用最上级文档的可见窗口
  root: null,
  // 同 margin,可以是 1、2、3、4 个值,允许时负值。
  // 如果显式指定了跟元素,该值可以使用百分比,即根元素大小的百分之多少。
  // 如果没指定根元素,使用百分比会出错。
  rootMargin: "0px",
  // 触发回调函数的临界值,用 0 ~ 1 的比率指定,也可以是一个数组。
  // 其值是被观测元素可视面积 / 总面积。
  // 当可视比率经过这个值的时候,回调函数就会被调用。
  threshold: [0],
});

  

iframe 魔法

设计 IntersectionObserver 的时候,我们着重考虑了广告服务和社交网络组件的需要,他们经常会内嵌在 iframe 里。使用 IntersectionObserver 可以简单的知道他们是否可见。 如果 iframe 在观测它内部的某个元素,滚动 iframe 本身或者滚动 iframe 外层的窗口都会在何时的时间触发回调函数。 在后一种情况,rootBounds 应该被设置为 null 以防止跨域泄漏数据。

IntersectionObserver 不是干什么的

有一点要记住:IntersectionObserver 不是完美精确到像素级别,也不是低延时性的。 使用它实现类似依赖滚动效果的动画注定会失败,因为回调函数被调用的时候那些数据——严格来说——已经过期了。 这篇说明 有更多IntersectionObserver 的用法细节。

我在回调函数中可以做多少工作?

简单的说:在回调函数中花大量时间会让你的 app 卡顿。所有的最佳实践在这里都适用。

去使用它吧

浏览器对于 IntersectionObserver 的支持度仍然较低,它不会再每个地方都正常工作。 在此期间,WICG 正在给它开发 polyfill。 当然,你没法获得原生实现给你的性能提升。

时间: 2024-12-20 08:26:28

使用IntersectionObserver更高效的监视某个页面元素是否进入了可见窗口的相关文章

New UI-<merge>标签减少视图层级,让布局更高效

New UI-<merge>标签减少视图层级,让布局更高效  --转载请注明出处:coder-pig,欢迎转载,请勿用于商业用途! 小猪Android开发交流群已建立,欢迎大家加入,无论是新手,菜鸟,大神都可以,小猪一个人的 力量毕竟是有限的,写出来的东西肯定会有很多纰漏不足,欢迎大家指出,集思广益,让小猪的博文 更加的详尽,帮到更多的人,O(∩_∩)O谢谢! 小猪Android开发交流群:小猪Android开发交流群群号:421858269 新Android UI实例大全目录:http://

5年以下的软件测试人,如何学习才会更高效?

? 对于很多工作年龄在5年以下的软件测试人来说,“学习”是个刚需.但与此同时,满世界看了一堆干货听了一堆课程之后并无卵用的状态却并不少见. 作为软件测试行业的一名老鸟,我想要跟大家探讨一个有趣的问题——5年以下的软件测试人到底该选择如何学习,才会更高效?回首我的八年职业发展之路,有过触碰到天花板时的困惑,也遇到过在人生岔路口选择时的彷徨不安. 今天结合我多年的职业成长历程,从几个角度来依次探讨下这个话题——从技术成长,非技术成长,心灵成长三个方面谈谈我的一些认识和理解,同时也想分享一下我的一些职

这些小工具让你的Android 开发更高效

在做Android 开发过程中,会遇到一些小的问题,虽然自己动手也能解决,但是有了一些小工具,解决这些问题就得心应手了,今天就为大家推荐一下Android 开发遇到的小工具,来让你的开发更高效. Vysor Vysor 是一个可以将手机的屏幕投影到电脑上,当然也可以操作,当我们做分享或者演示的时候,这个工具起到了作用. Vector Asset Android Studio 在1.4 支持了VectorAsset,所谓VectorAsset:它可以帮助你在Android 项目中添加Materia

除了cPickle,cjson外还有没有更高效点的序列化库了

除了cPickle,cjson外还有没有更高效点的序列化库了 http://blog.csdn.net/chen_lovelotus/article/details/7228745 msgpack最快,而且是跨语言的,二进制,但只能打包简单的list,dict,int,string,unicode,在memcache之类的 字符串协议里会有问题. marshal其次,也是二进制的,可以打包大多数python对象,缺点同msgpack,另文档上说各版本的python实现会不一样,但我这测下 来三台

如何使程序更高效的执行?

在codewars网站做题,做到一下一道题. var add = function (a, b) { return a + b; }; var lazy_sum = make_lazy(add, 2, 3); Test.expect(lazy_sum() === 5, 'Evaluates the expression when required');//这个是codewars网站特有的测试代码,自己运行要另行写 var double = function (n) { return n * 2;

25个让Java程序员更高效的Eclipse插件

Eclipse提供了一 个可扩展插件的开发系统.这就使得Eclipse在运行系统之上可以实现各种功能.这些插件也不同于其他的应用(插件的功能是最难用代码实现的).拥有合 适的Eclipse插件是非常重要的,因为它们能让Java开发者们无缝的开发基于J2EE和服务的应用程序.Eclipse的插件也能帮助他们开发不同 应用架构上的程序. 下面列出来的是25个最好的免费Eclipse插件,可以让开发者更高效的工作 . 提高代码质量的插件 1. FindBugs FindBugs可以帮你找到Java代码

如何更高效地定制你的bootstrap

bootstrap已经作为前端开发必不可少的框架之一,应用bootstrap使得我们对布局.样式的设定变得非常简单.但bootstrap提供的默认样式往往不能满足我们的需求,从而定制化bootstrap成为我们经常需要做的工作,本文就如何更高效更可维护地定制bootstrap做一下探讨. 如下图,在你的button 中加入bootstrap的class: btn btn-primary,就可以将默认的button(左边)变成右边的样式. 可如果我们想应用自己的样式呢?比如我们想要拥有圆角的but

AS--&gt;如何更高效的使用 Gradle, 快速build apk

版权声明:欢迎转载,转载请注明出处;http://blog.csdn.net/angcyo 看本文之前,推荐先看我之前写的一篇文章: 传送门 日前Android Stuido 已经更新到 2.0.0 preview 5了; 虽然官网说, gradle 的速度, 大幅度提升,然而并没有什么卵用; 不知道是不是个人问题, 使用 install run 功能之后, 修改逻辑代码,偶尔会出现不生效的情况; 就是代码,明明改了,但是执行结果却和之前的一样; 但是,xml布局文件,修改之后,效果真的很明显,

MySQL中比like语句更高效的写法locate position instr find_in_set

你是否一直在寻找比MySQL的LIKE语句更高效的方法的,下面我就为你介绍几种. LIKE语句 SELECT `column` FROM `table` where `condition` like`%keyword%' 事实上,可以使用 locate(position) 和 instr这两个函数来代替 LOCATE语句 SELECT `column` from `table` where locate('keyword',`condition`)>0 或是 locate 的別名 positio