写在前面

在移动端,有个很重要的概念,叫做懒加载,适用于一些图片资源特别多,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. js 格式为2018-08-25 11:46:29 的日期比较方法

    //判断日期,时间大小 function compareTime(startDate, endDate) { if (startDate.length > 0 && endDat ...

  2. MP实战系列(七)之集成springboot

    springboot是现在比较流行的微服使用的框架,springboot本质上就是将spring+springmvc+mybatis零配置化,基本上springboot的默认配置符合我们的开发.当然有 ...

  3. PAT A1115 Counting Nodes in a BST (30 分)——二叉搜索树,层序遍历或者dfs

    A Binary Search Tree (BST) is recursively defined as a binary tree which has the following propertie ...

  4. Photoshop 基础六 图层

    图层就像一张张叠在一起的胶片,最上层的图像挡住下面的图像,使之看不见 上层图像中没有像素的地方为透明区域,通过透明区域可以看到下一层的图像 图层是相对独立的,在一个图层编辑时,不影响其它图层 每次只能 ...

  5. char.IsLetter的使用

    先看一下下面的代码,大家会觉得控制台输出什么? 输出:Chiantxt  .对吗? 因为你看到char.IsLetter这个方法的文字的注释是这样写的: 但实际上输出的结果是这样的: ??? 怎么还输 ...

  6. WPF中的DoubleAnimation

    原文:WPF中的DoubleAnimation WPF中的DoubleAnimation                                                         ...

  7. Luogu P1129 [ZJOI2007]矩阵游戏

    题目意思还是比较直观的,而且这个建模的套路也很明显. 我们首先考虑从主对角线可以转移到哪些状态. 由于每一次操作都不会把同一行(列)的黑色方块分开.因此我们发现: 只要找出\(n\)个黑色棋子,让它们 ...

  8. CSS 列表实例

    CSS 列表属性允许你放置.改变列表项标志,或者将图像作为列表项标志.CSS 列表属性(list)属性 描述list-style 简写属性.用于把所有用于列表的属性设置于一个声明中.list-styl ...

  9. Linux下开源邮件系统Postfix+Extmail+Extman环境部署记录

    一.基础知识梳理MUA (Mail User Agent) MUA 既是"邮件使用者代理人",因为除非你可以直接利用类似 telnet 之类的软件登入邮件主机来主动发出信件,否则您 ...

  10. MySQL针对Swap分区的运维注意点

    Linux有很多很好的内存.IO调度机制,但是并不会适用于所有场景.对于运维人员来说,Linux比较让人头疼的一个地方是:它不会因为MySQL很重要就避免将分配给MySQL的地址空间映射到swap上. ...