Intersection Observer 简介

Intersection Observer API提供了一种异步观察目标元素与祖先元素或顶级文档viewport的交集中的变化的方法。这使得以往较难实现的功能,更加简单,例如,监听图片元素,在适当的时候懒加载图片。

例子

先看下,下面是一个简单的小例子

<div id="e1" style="background-color:black;width:100%;height:500px;">
  <ul style="color:white;">
    <li>boundingClientRect:<span id="el-boundingClientRect"></span></li>
    <li>intersectionRatio:<span id="el-intersectionRatio"></span></li>
    <li>intersectionRect:<span id="el-intersectionRect"></span></li>
    <li>isIntersecting:<span id="el-isIntersecting"></span></li>
    <li>rootBounds:<span id="el-rootBounds"></span></li>
    <li>time:<span id="el-time"></span></li>
  </ul>
</div>
var observer = new IntersectionObserver((entries,observer) => {
  // 我只监听了一个对象
  let entry = entries[0]
  document.querySelector("#el-boundingClientRect").innerHTML = JSON.stringify(entry.boundingClientRect);
  document.querySelector("#el-intersectionRatio").innerHTML = JSON.stringify(entry.intersectionRatio);
  document.querySelector("#el-intersectionRect").innerHTML = JSON.stringify(entry.intersectionRect);
  document.querySelector("#el-isIntersecting").innerHTML = JSON.stringify(entry.isIntersecting);
  document.querySelector("#el-rootBounds").innerHTML = JSON.stringify(entry.rootBounds);
  document.querySelector("#el-time").innerHTML = JSON.stringify(entry.time);
  //document.querySelector("el-target").innerHTML = entry.target;
}, {
  threshold : [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
});
observer.observe(document.querySelector("#e1"));

简书上由于安全原因,不能植入js脚本,所以无法预览,如果你希望预览例子,请前往我的博客

构造器

通过例子,我们可以看到Intersection Observer需要通过构造器来创建,即new IntersectionObserver(callback[, options]),参数有两部分组成,一个必传的回调函数以及一个可选的配置参数

回调Callback

Intersection Observer的翻译就是“交点观察”,因此,他的回调就成了重点。当观察元素与根元素之间的交叉状态发生变化时,它会将这部分信息反馈回来--通过回调告知

在回调函数里,我们会接受到两个对象,发生状态变化的元素集合以及监听者本身(注意:创建时,会将所有被观察元素的状态传递过来),监听者本身我们可以通过它增加或者减少监听的元素或者销毁自身(后续讲),这里我们更关注观察元素

function callback(entries,observer) {
  entries -> 一系列被观察的元素
  observer -> 观察者
}

entries 是一个 IntersectionObserverEntry 对象的数组,IntersectionObserverEntry 包换以下元素(来自MDN)

  • boundingClientRect: 返回包含目标元素的边界信息的DOMRectReadOnly. 边界的计算方式与 Element.getBoundingClientRect() 相同
  • intersectionRatio: 返回intersectionRect 与 boundingClientRect 的比例值
  • intersectionRect: 返回一个 DOMRectReadOnly 用来描述根和目标元素的相交区域
  • isIntersecting: 返回一个布尔值, 如果目标元素与交叉区域观察者对象(intersection observer) 的根相交,则返回 true .如果返回 true, 则 IntersectionObserverEntry 描述了变换到交叉时的状态; 如果返回 false, 那么可以由此判断,变换是从交叉状态到非交叉状态
  • rootBounds: 返回一个 DOMRectReadOnly 用来描述交叉区域观察者(intersection observer)中的根
  • target: 与根出现相交区域改变的元素 (Element)
  • time: 返回一个记录从 IntersectionObserver 的时间原点(time origin)到交叉被触发的时间的时间戳(DOMHighResTimeStamp)

具体的值是多少,我们可以在最上面的例子中看到,需要注意传递过来的对象都是只读(毕竟回调,只是通知你发生变化了)

参数options

我们可以通过 options 配置 IntersectionObserver,他包含以下几项配置

  • root: 监听元素的祖先元素Element对象,其边界盒将被视作视口。目标在根的可见区域的的任何不可见部分都会被视为不可见。默认情况下文档视口会作为root
  • rootMargin: 一个在计算交叉值时添加至根的边界盒(bounding_box)中的一组偏移量,类型为字符串(string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和CSS 中的margin 属性等同。默认值是"0px 0px 0px 0px"。
  • threshold: 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组0.0到1.0之间的数组。若指定值为0.0,则意味着监听元素即使与根有1像素交叉,此元素也会被视为可见. 若指定值为1.0,则意味着整个元素都交叉时视为可见。阈值的默认值为0.0。

在上面的例子中,我未修改root与rootMargin,你可以将浏览器的窗口作为可见区域,threshold定义了一系列数组,意味着到达那些交叉比时触发回调

属性

在创建或者回调函数中,我们可以得到 IntersectionObserver 对象,他包含以下属性:

  • root: 所监听对象的具体祖先元素(element)。如果未传入值或值为null,则默认使用顶级文档的视窗
  • rootMargin: 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。此属性返回的值可能与调用构造函数时指定的值不同,因此可能需要更改该值,以匹配内部要求。所有的偏移量均可用像素(pixel)(px)或百分比(percentage)(%)来表达, 默认值为"0px 0px 0px 0px"
  • thresholds: 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification)。如果构造器未传入值, 则默认值为0

IntersectionObserver 的属性也都是只读,他在创建之后不支持修改

方法

IntersectionObserver 通过以下方法添加或者取消监听元素

  • IntersectionObserver.disconnect() 使IntersectionObserver对象停止监听工作。
  • IntersectionObserver.observe() 使IntersectionObserver开始监听一个目标元素。
  • IntersectionObserver.takeRecords() 返回所有观察目标的IntersectionObserverEntry对象数组。
  • IntersectionObserver.unobserve() 使IntersectionObserver停止监听特定目标元素。

真实的例子

简书上由于安全原因,不能植入js脚本,所以无法预览,如果你希望预览例子,请前往我的博客

懒加载

IntersectionObserver 可以很简单的实现图片懒加载,开头就提到的,看下面很少的代码,我们就实现了懒加载的功能(目前已经好像图片懒加载框架使用它了,比如lozad.js)

<img id="e2-lazy" load-url="/images/avatar.jpg" />
var observer = new IntersectionObserver((entries,observer) => {
  let entry = entries[0]
  if (entry.isIntersecting) {
    let el = entry.target;
    el.src = el.getAttribute('load-url');
  }
});
observer.observe(document.querySelector("#e2-lazy"));

TOC 自动定位

我的博客(Cake与NexT主题)的侧边栏,它使用的就是IntersectionObserver。不过在使用时,也遇到了些问题。比如,IntersectionObserver 在浏览器中的优先级不高(高效,必然也有缺陷),如果你很快的移动,可能无法触发

我们(NexT团队)第一次尝试时,并未考虑优先级的事情,我们根据标题元素的是否在浏览器窗口可见计算其toc的位置,但如果快速滑动,那么toc的定位就会错乱

后续,我们调整,扩大根窗口的区域(与文章一样长)的方式,那么我们只需要判断是不是已经交叉即可,即便滑动太快也无需担心

相关的代码可以见:

本文作者: Mr.J
本文链接: https://www.dnocm.com/articles/beechnut/intersection-observer-info/
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

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

推荐阅读更多精彩内容