写在前面

在移动端,有个很重要的概念,叫做懒加载,适用于一些图片资源特别多,ajax数据特别多的页面中,经常会有动态加载数据的场景中,这个时候,我们通常是使用监听scroll或者使用setInterval来判断,元素是否进入视图,其中scroll由于其特别大的计算量,会有性能问题,而setInterval由于其有间歇期,也会出现体验问题。

1. 关于IntersectionObserver

浏览器的开发商,估计也发现了这个问题,所以在2016年初,chrome51率先提供了一个新的API,就是IntersectionObserver,它可以用来监听元素是否进入了设备的可视区域之内,而不需要频繁的计算来做这个判断。

毕竟是一个新兴的API,所以浏览器的支持性并不好,这里可以看看当前浏览器对于IntersectionObserver的支持性:IntersectionObserver-canuse 

虽然当前受限于浏览器的支持性,该方法还不能用于生产环境中,但却不影响我们去先了解一下这个令人兴奋的API,所以在接下来的文章中,就先来一步步的看看,为我们带来了哪些好处吧。

2. API简介

API的调用非常简单:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
var observer = new IntersectionObserver(callback,options);

IntersectionObserver支持两个参数:

    1. callback是当被监听元素的可见性变化时,触发的回调函数
  1. options是一个配置参数,可选,有默认的属性值

接下来,就看一个官方的示例代码:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
//初始化一个实例
var observer = new IntersectionObserver(changes => {
for (const change of changes) {
console.log(change.time);
// Timestamp when the change occurred
// 当可视状态变化时,状态发送改变的时间戳
// 对比时间为,实例化的时间,
// 比如,值为1000时,表示在IntersectionObserver实例化的1秒钟之后,触发该元素的可视性变化 console.log(change.rootBounds);
// Unclipped area of root
// 根元素的矩形区域信息,即为getBoundingClientRect方法返回的值 console.log(change.boundingClientRect);
// target.boundingClientRect()
// 目标元素的矩形区域的信息 console.log(change.intersectionRect);
// boundingClientRect, clipped by its containing block ancestors,
// and intersected with rootBounds
// 目标元素与视口(或根元素)的交叉区域的信息 console.log(change.intersectionRatio);
// Ratio of intersectionRect area to boundingClientRect area
// 目标元素的可见比例,即intersectionRect占boundingClientRect的比例,
// 完全可见时为1,完全不可见时小于等于0 console.log(change.target);
// the Element target
// 被观察的目标元素,是一个 DOM 节点对象
// 当前可视区域正在变化的元素 }
}, {}); // Watch for intersection events on a specific target Element.
// 对元素target添加监听,当target元素变化时,就会触发上述的回调
observer.observe(target); // Stop watching for intersection events on a specific target Element.
// 移除一个监听,移除之后,target元素的可视区域变化,将不再触发前面的回调函数
observer.unobserve(target); // Stop observing threshold events on all target elements.
// 停止所有的监听
observer.disconnect();

详情请参考:IntersectionObserver

俗话说,纸上得来终觉浅,还是看个小示例吧:第一场实战演练

3. callback和options

IntersectionObserver这个方法中,最重要的一个参数就是callback参数,如前面的示例中看到的,那么我们后面再来说另外一个参数,options吧。

现在就先来看一下options的参数:

options是可以选择设置的,即便我们不设置该参数,也会有一个默认的属性,这个默认的属性,就定义在了IntersectionObserver的原型链中,那么接下来看看有哪些默认的属性和方法:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
disconnect()
// function,没有参数。
// 停止实例的继续监听,在前面的callback回调函数中,已经有看到了 observe(target)
// function,传入一个参数,原生的DOM对象,一个将要被监听的DOM元素
// 添加一个监听元素, root
// DOM元素,但默认值为null,也就是视口区域
// 表示监听的可视区域为整个视口,也就是浏览器的可视区域
// 如果设置了DOM元素,那么视口就变为该元素(即,就算元素在屏幕的可视区域,但是不在该DOM元素的可视区域,仍然不会触发可视性变化) rootMargin
// 类似于css的margin属性,可以设置四个属性。
// 该属性默认值为0px 0px 0px 0px,如果设置之后,会影响触发回调的时间 takeRecords
// 该属性,目前没有找到是干嘛的,返回一个数组,而且一直为空数组 thresholds
// Array
// 默认值为[0],
// 当元素的进入可视区域的百分比达到这个参数的设置值得时候
// 就会触发IntersectionObserver实例中的callback的回调函数
// 具体后面再看啦 unobserve(target)
// 解除target的元素的绑定

其中,rootrootMarginthresholds这三者是属性,说到这里,就是想引入前面调用的时候的options的取值。在IntersectionObserver的第二个参数options中,可以通过设置这三个属性,来改变IntersectionObserver实例的一些表现。

接下来我们就一个个的看一下它们的具体效果。

3.1 root参数

根元素,可视性以哪个root元素,作为参考系,如果不设置,那么可视区域,就是整个视图的区域,那么只要元素在该区域可视之后,就可以触发监听的回调函数。

俗话再说,事实胜于雄辩,再来实战吧:

第二场实战演练(root设置为一个DIV)

第二场实战中,设置root为一个DIV元素,并添加了两个被监听的元素,其中,root内部的元素,可视性变化的时候,会触发callback,而root外部的元素,即便是一直在可视区域,也不会出现触发可视性的变化,这就是root的效果。

再做一个对比,如果不设置的时候:

第三场实战演练(root为默认值)

root参与设置监听的区域,就比如上面的两个示例,如果给root设置了为一个DIV,那么所有被监听的目标元素,只有那些root元素的子元素,当其可见性变化时,才会触发该实例的回调函数。在该root元素之外的目标元素,可见性变化时,是无法被监听到的。

并且,还有一个问题,如果root设置为一个DIV,而这个DIV的高度,是高于设备的可视区域的,那么当滚动这个区域时,元素在没有还没有进入设备的可视区域的时候,已经进入了root的可视区域了,那么依然也会触发可视性改变的回调函数,在本小节上面的两个示例中,都可以测试出该种状态。

3.2 rootMargin属性

关于这个属性,我们可以理解为更精确的配置参数,它是设置在root元素上的一个margin属性,其使用方法与CSS中的margin属性是完全相同的,虽然这个命名为rootMargin,也是设置在root属性上的,但真正起作用是作用在元素上的

如下图所示: 

如图,root元素就是图中的黑色区域,给它设置一个rootMargin:10px的属性,就是如图中,黑色区域,和边框之间的间隔区域。

假设现在监听元素ele(假设为上图中,红色元素区域)的可视性变化,那么如果我们不设置rootMargin的话,只有当ele元素,进入图中黑色区域的时候,其可视性才会改变,而这里,我们设置了rootMagrin的值,如图所示,当ele进入到边框内部,还没有进入到黑色区域时,就可以触发可视性变化的。

或者也可以这样理解,root元素,多了一个margin属性,如果没有这个margin属性,ele元素只有与root元素开始交叉时才会触发可视性的变化,而这个rootMargin属性的话,就是当ele元素与root元素的外边距交叉时,就会触发ele元素的可视性变化。

那么rootMagrin的好处在哪里呢?可以理解为,懒加载的预加载,比如图片的懒加载,不是等图片元素进入可视区域之后,才去加载,而是检测到,离进入可视区域,在一定范围内部的时候,就提前加载(叫做预加载吧),这样,当真的进入视图之内,说不定已经加载好了,可以大大的提升整个产品的体验。

第四场实战演练

虽然说,rootMargin的设置方法,与CSS中的margin属性设置方法完全相同,但是其设置的值,却不完全相同,比如,rootMargin属性就不能支持“em”,“rem”等单位的设置。所以设置时,尽量使用一些基本的单位,比如“px”,”%“等。

3.3:threshold属性

在前面的示例中,每个元素只有在刚出现和刚离开的时候(change.intersectionRatio=0的变化时),才会触发可视性的变化,如果只是这样的话,那该方法就显得过于死板了,一点都不灵活。

所以,在初始化设置参数时,还可以传入一个threshold的参数,用来设置当change.intersectionRatio属性,达到指定值时,也会触发回调函数。

threshold的默认值是:[0],即只有在开始进入,或者是完全离开视图区域时,才会触发。

这里有个很疑问的地方,为何在实例属性中,是thresholds的属性,而在设置时,却是设置threshold的值呢,比如设置和读取:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
var observer = new IntersectionObserver(_observer, {
root : $(“#containter”)[0],
threshold:[0,0.25,0.5,0.75]
//设置时,不是个复数
}); //查看实例属性时,却是复数。
observer.thresholds

继续看DEMO吧:第五场实战演练(threshold属性)

注意:虽然,上述的三个属性,都是属于实例中的属性,但是这三个属性,只是可读的,当实例化之后,这三个属性就是不可更改的。有兴趣的可以自己试试。

3.4 回调函数

前面把options的参数,介绍了一下,接下来再来看看callback能为我们提供些什么信息吧。

可以看第二小节中的示例代码,可以看到,在callback中,是传入一个数组,数组中的每一个元素,都代表一个可视性变化的DOM元素,它包含该元素的一些基本信息和变化的信息。

看如下的代码:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
//初始化一个实例
var observer = new IntersectionObserver(changes => {
for (const change of changes) {
console.log(change.time);
console.log(change.rootBounds);
console.log(change.boundingClientRect);
console.log(change.intersectionRect);
console.log(change.intersectionRatio);
console.log(change.target);
}
}, {});

参数的含义,在本篇开头的地方,示例也在前面引入过来,这里继续引入前面的一个示例吧:再看第一场实战演练

其中,有三个参数是用来获取位置信息的,rootBoundsroot元素的位置信息),boundingClientRecttarget元素的位置信息),intersectionRect(进入交叉显示区域的位置信息,与intersectionRatio属性有关)。如果要更好的理解,需要看一下前面的示例,在控制台对比这三个数据,才能更好的理解。

4. 其他

前面我们说的都是一些基础的元素,基础的属性,那么如果设置了positionmargindisplayvisibilityclip等属性呢?对于元素的可视性,会有什么影响吗?

这个希望有兴趣了解的,可以自己去尝试一把,来一次实战如何?

IntersectionObserver实例是一个异步的实例,它只有在浏览器空闲的状态下才会触发,如果浏览器当前的事件队列中,有一系列的回调函数正在等待处理,该方法是不会被执行到的,只有当浏览器的事件队列为空,浏览器在空闲的时候,才会执行该方法。

就异步的处理方式来看,该APIrequestIdleCallbackAPI,效果是一样的,都是在浏览器空闲的时候,才会被执行,他们与计时器的原理是不一样的,计时器的原理是在指定的时间之后,就把处理函数直接推入到执行队列中去,而这两个方法,就算已经被触发了(或者说,调用过来,依然不会出现在函数执行队列中)。

这是浏览器的一个优化,考虑用户体验的同时,也在考虑浏览器的性能消耗。

5. 应用

  1. 预加载(滚动加载,无限加载)
  2. 懒加载(图片后加载)
  3. TAB滚动跟随

暂时能想到的应用场景,就是上面提到的这些个场景

关于这些应用的实战,就靠你啦,去实战一下吧。

6. 总结

在当前判断可视性的方法,基本就是监听scroll事件,或者是计时器循环判断来做这个判断,但是由于其高频的计算频率,会导致浏览器性能的损失,尤其是,如果一个同一个页面中,有多个地方,需要这样的判断,那么就需要绑定多个scroll事件,或者有多个计时器在轮询的话,那么对性能的损失就更为客观了。

虽然现在的浏览器性能一直在增强,但是也有更多的消耗性能的比较炫的技术在产生,它们依然在占据着浏览器的大量的计算内存,所以,尽量在可以节省性能的时候,就节省一下性能吧。

而该方法给我们提供了一个更简单直接,性能更好的解决方案,希望以后的浏览器,可以越来越广泛的支持吧。

本篇到此为止,感谢阅读。

如果您发现文中有描述错误或者不当的地方,请留言指出,不胜感激,谢谢!

本原文地址:http://www.zhangyunling.com/?p=811

IntersectionObserver简介的更多相关文章

  1. IntersectionObserver API

    温馨提示:本文目前仅适用于在 Chrome 51 及以上中浏览. 2016.11.1 追加,Firefox 52 也已经实现. 2016.11.29 追加,Firefox 的人担心目前规范不够稳定,未 ...

  2. ASP.NET Core 1.1 简介

    ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...

  3. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

  4. Cassandra简介

    在前面的一篇文章<图形数据库Neo4J简介>中,我们介绍了一种非常流行的图形数据库Neo4J的使用方法.而在本文中,我们将对另外一种类型的NoSQL数据库——Cassandra进行简单地介 ...

  5. REST简介

    一说到REST,我想大家的第一反应就是“啊,就是那种前后台通信方式.”但是在要求详细讲述它所提出的各个约束,以及如何开始搭建REST服务时,却很少有人能够清晰地说出它到底是什么,需要遵守什么样的准则. ...

  6. Microservice架构模式简介

    在2014年,Sam Newman,Martin Fowler在ThoughtWorks的一位同事,出版了一本新书<Building Microservices>.该书描述了如何按照Mic ...

  7. const,static,extern 简介

    const,static,extern 简介 一.const与宏的区别: const简介:之前常用的字符串常量,一般是抽成宏,但是苹果不推荐我们抽成宏,推荐我们使用const常量. 执行时刻:宏是预编 ...

  8. HTTPS简介

    一.简单总结 1.HTTPS概念总结 HTTPS 就是对HTTP进行了TLS或SSL加密. 应用层的HTTP协议通过传输层的TCP协议来传输,HTTPS 在 HTTP和 TCP中间加了一层TLS/SS ...

  9. 【Machine Learning】机器学习及其基础概念简介

    机器学习及其基础概念简介 作者:白宁超 2016年12月23日21:24:51 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...

随机推荐

  1. oracle 查询 归档日志最大值和平均值

    select max(ss.size_GB), avg(ss.size_GB)  from (select s.*, rownum rn2          from (select a.*      ...

  2. 【Codeforces 696D】Legen...

    Codeforces 696 D 题意:给\(n\)个串,每个串有一个权值\(a_i\),现在要构造一个长度为\(l\leq 10^{14}\)的串,如果其中包含了第\(i\)个串,则会得到\(a_i ...

  3. Android学习之基础知识一

    一.Android的系统架构: 1.Linux内核层:提供Android硬件的各种驱动(显示驱动,音频驱动,蓝牙驱动,WiFi驱动等等) 2.系统运行库层:提供各种特性支持(数据库支持,绘图支持,浏览 ...

  4. vue-用Vue-cli从零开始搭建一个Vue项目

    Vue是近两年来比较火的一个前端框架(渐进式框架吧). Vue两大核心思想:组件化和数据驱动.组件化就是将一个整体合理拆分为一个一个小块(组件),组件可重复使用:数据驱动是前端的未来发展方向,释放了对 ...

  5. dotnetcore/CAP

    CAP带你轻松玩转Asp.Net Core消息队列 CAP是什么? CAP是由我们园子里的杨晓东大神开发出来的一套分布式事务的决绝方案,是.Net Core Community中的第一个千星项目(目前 ...

  6. NIO之缓冲区

    NIO引入了三个概念: Buffer 缓冲区 Channel 通道 selector 选择器 1.java.io优化建议 操作系统与Java基于流的I/O模型有些不匹配.操作系统要移动的是大块数据(缓 ...

  7. subprocess.Popen指令包含中文导致乱码问题解决

    其实解决起来非常简单,如果了解到Windows中文系统编码为GB2312的话 只需将你包含中文的指令字符串编码为GB2312即可 cmd = u'cd 我的文档' cmd.encode('gb2312 ...

  8. 树的最长链-POJ 1985 树的直径(最长链)+牛客小白月赛6-桃花

    求树直径的方法在此转载一下大佬们的分析: 可以随便选择一个点开始进行bfs或者dfs,从而找到离该点最远的那个点(可以证明,离树上任意一点最远的点一定是树的某条直径的两端点之一:树的直径:树上的最长简 ...

  9. javaScript常用API合集

    节点 1.1 节点属性 Node.nodeName   //返回节点名称,只读 Node.nodeType   //返回节点类型的常数值,只读 Node.nodeValue  //返回Text或Com ...

  10. B. Diagonal Walking v.2

    链接 [https://i.cnblogs.com/EditPosts.aspx?opt=1] 题意 二维平面从原点出发k步,要到达的点(x,y),每个位置可以往8个方位移动,问到达目的地最多可以走多 ...