使用IntersectionObserver来在提升一下性能

一直以来我们要监控2个元素的相对位置,总是比较麻烦的,而且之前也只能通过js以及每个元素的top值来控制,这也极易拖慢整个网站的性能。然而,随着网页的发展,对上述检测的需求也随之增加,多种情况下都需要用到元素交集变化的信息。如:

  • 当页面滚动时,懒加载图片或其他内容。
  • 实现”无限滚动“功能页面
  • 可以统计一些广告元素的曝光情况
  • 根据用户滚动位置来控制执行任务或者动画

相对于过去,我们在检测交集时,需要涉及到事件监听,以及对每个目标元素执行Element.getBoundingClientRect()方法来获取所需信息。然后通过每个元素的信息来检测元素的交叉。

而且在类似无限滚动的场景下,我们不仅仅需要获取一些元素的位置信息,更需要监控例如scroll等事件,并且很多情况下,也需要依赖一些第三方库来进行监控,并且在第三方库中具体执行了什么,我们并不知晓,这样很是影响性能,并且得到的体验也不是特别友好。

那在这种背景下,我们有更好的方法吗?

IntersectionObserver概念

Intersection Observer的出现,解决了这个问题,Intersection Observer API 会在浏览器注册一个观察者,并且可以设定据地要观察的目标(target),当目标元素(target)以及根元素或者指定的外层元素(root元素)相互交叉的时候触发事件。

用法介绍

//首先我们要先创建观察者
var observer = new IntersectionObserver(callback, options);
//接下来我们要设置具体观察哪个目标
let target = document.querySelector('#id');
observer.observe(target);

我们在来看看创建监控函数时要传递的options中的参数:

参数名 描述 类型 默认值
root 指定根目录,也就是当目标元素显示在这个元素中时会触发监控回调 Dom元素 null,即浏览器窗口
rootMargin 类似于css的margin,设定root元素的边框区域。值与css的margin一样“10px 10px 10px 10px” 对应“top right bottom left" String 0
threshold 可以是单一的number也可以是一个number数组,这个值可以控制target元素进入root元素中可见性超过的阙值,当达到这个值则会出发函数,并且我们也可以使用数据来让元素在进入时在不同的可见度返回多次值 number或array 0

实战

我们先来看一个最简单的操作:
这是我们的html:

//我们创建三个容器,并且把第三个容器中的p定位目标
 <div class="con">
     <div class="page red">
         <p>我是第一页</p>
     </div>
     <div id="scrollArea" class="page green">
         <p>我是第二页</p>
     </div>
     <div class="page blue">
         <p class="listItem">我是第三页</p>
     </div>
</div>

接下来我们创建观察者,以及设定要观察的目标:

var observer = new IntersectionObserver((entries, observer) => {
    alert("进入");
}, {});
let target = document.querySelector('.listItem');
observer.observe(target);
全部使用默认参数的简单效果

我们来设定一下对应的root,让target的目标换成一个指定的元素。

//我们设定一个外层容器,并且把这个容器先搞小一点,方便查看:
var options = {
    root: document.querySelector('.con')
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

我们可以指定对应的被观察者要进入哪个root根容器中才会触发回调。

我们设定了一个root值之后的效果

接下来我们在来尝试更复杂的去控制:
需要注意的是,当我们这样来设置rootMargin时,会出现错误:

var options = {
    rootMargin: "100px 0 0 0"
}
rootMargin设置错误

虽然其规范与css得我margin一样,但是当值是0的时候,我们是无法直接使用0的,要添加上单位,如px、rem、em等,正确写法如下:

var options = {
    rootMargin: "1
00px 0px 0px 0px"
}

我们来看一下这个图,当设置rootMargin时,就相当于把对应的元素放大,如下图,root为黄色区域,但是我们设定了rootMargin,实际上对于target来说,整个蓝色区域都会认定为root的监控区域,当target进入蓝色区域机会触发回调。

rootmargin中root与target关系

让我们来看看如何设定吧:

var options = {
    root: document.querySelector('.con'),
    rootMargin: "0px 0px 100px 0px"
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

注意:我们设置的margin是root的而不是target的,所以在一下的这个demo中,当target往上滑动时,target具体root还差100px时就触发了回调,这个要设定为bottom而非top。

设置rootMargin之后的效果

我们来看看threshold属性:

threshold 此参数的范围为0.0-1.0,并且我们可以设置一个number值,也可以设置一个number的Array数组,来触发多次回调。

首先我们设定一个触发值:

var options = {
    root: document.querySelector('.con'),
    threshold: 1.0
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

我们设定,当target全部显示到root中时才会触发回调:

设定了一个值

这次我们来尝试设置多次触发点:

var options = {
    root: document.querySelector('.con'),
    threshold: [0, 0.5, 1.0]
}
var observer = new IntersectionObserver((entries, observer) => {
    console.log("进入");
}, options);
let target = document.querySelector('.listItem');
observer.observe(target);

我们设定了在target开始出现,出现一半,以及全部漏出的时候都会触发,所以应该会触发三次回调,看看效果呢。

多个触发点控制

截止到此,我们应该已经知道IntersectionObserver的使用方法。

IntersectionObserver回调中的回参

既然我们知道此API的使用方式,那么我们也一定很好奇此函数具体返回了什么。
函数的callback会返回2个值: [IntersectionObserverEntry] 和 IntersectionObserver。

  1. IntersectionObserverEntry: 提供了target和root交叉之后的一些信息,此值无法主动创建,但是可以通过IntersectionObserver.takeRecords()来获取。
    参数内容如下:

    IntersectionObserverEntry

  2. IntersectionObserver: 提供了当前创建的观察者IntersectionObserver的所有信息。

    IntersectionObserver

尾声

这个API,可能大家遇到或者听说过的都不多,不过如果有幸你们看到了它,那它可能会给你的代码带来一些新的优化灵感,如果能达到这样的效果,那么这篇文章就有了其存在的价值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,096评论 1 32
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,490评论 1 11
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,472评论 1 45
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,748评论 1 92
  • 清晨,行在上班的路上,这是一条漫长的路。这是我经常走的路,也是我将要走很长一段时间的路!出家门时,还是一片...
    道武先生阅读 268评论 0 2