IntersectionObserver API 用法

220 阅读3分钟

文章首发:IntersectionObserver API 用法 | icodex

当我们说到图片懒加载、页面数据的滚动加载这些体验设计时,一般能够想到基于scroll事件,通过getBoundingClientRect方法获取元素相对于视口偏移量top,来判断元素是否可见,demo 如下

Edit jovial-snowflake-e42869

这种实现方式较为繁琐,但是现在我们有了IntersectionObserver API,可以大大简化这些通过计算元素偏移量来判断可视性的逻辑。

IntersectionObserver 介绍

W3C 在 2017-09-14 正式发布了IntersectionObserver API 草案,其功能上提供了检测目标元素在祖先元素或 viewport 内可视情况变化的方式,这种观测是异步的,保证了开发上的便捷和性能上的高效。

IntersectionObserver类型定义

IntersectionObserver本身是一个构造函数,需要通过new来初始化调用,基本使用方式如下:

 // 获取目标元素 DOM 对象
 const observeTarget = document.querySelector("#targetId");
 ​
 // 初始化IntersectionObserver实例
 let observer = new IntersectionObserver((entries) => {
   entries.forEach(e => {
     // e.isIntersecting 为 true,则目标元素变得可见
     if (e.isIntersecting) {
       
     }
   })
 }, {
   threshold: [0, 1]
 });
 ​
 // 开始监测目标元素
 observer.observe(observeTarget);
 ​
 // 取消监测
 observer.unobserve(observeTarget);
 observer.disconnect();

下面是详细的 TypeScript 类型定义:

 /*
  * 构造函数
  */
 declare var IntersectionObserver: {
     prototype: IntersectionObserver;
     new(callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;
 };
 ​
 /*
  * 构造函数初始参数1:回调函数
  */
 interface IntersectionObserverCallback {
     (entries: IntersectionObserverEntry[], observer: IntersectionObserver): void;
 }
 ​
 /*
  * 构造函数初始参数2:回调函数执行条件设定
  */
 interface IntersectionObserverInit {
     /*
      * 目标元素的包含元素,如果不指定则默认为 viewport
      */
     root?: Element | Document | null;
     /*
      * 目标元素相对于 viewport 或者包含元素的偏移量
      * 注意:负值表示目标元素在包含元素内部偏右或者偏下的位置
      */
     rootMargin?: string;
     /*
      * 目标元素触发回调函数的
      */
     threshold?: number | number[];
 }
 ​
 /*
  * IntersectionObserver对象属性和方法
  */
 interface IntersectionObserver {
     readonly root: Element | Document | null;
     readonly rootMargin: string;
     readonly thresholds: ReadonlyArray<number>;
     /*
      * 停止监测所有元素
      */
     disconnect(): void;
     /*
      * 开始监测目标元素
      */
     observe(target: Element): void;
     /*
      * 获取所有目标元素的监测对象
      */
     takeRecords(): IntersectionObserverEntry[];
     /*
      * 停止监测目标元素
      */
     unobserve(target: Element): void;
 }
 ​
 /*
  * 回调函数获取的目标元素的监测变量
  */
 interface IntersectionObserverEntry {
     /*
      * 目标元素在 viewport 内部的坐标
      */
     readonly boundingClientRect: DOMRectReadOnly;
     /*
      * 目标元素可见比例
      */
     readonly intersectionRatio: number;
     /*
      * 目标元素可见区域在 viewport 内部的坐标
      */
     readonly intersectionRect: DOMRectReadOnly;
     /*
      * 目标元素是否可见
      */
     readonly isIntersecting: boolean;
     /*
      * 目标元素的root元素可见区域的坐标
      */
     readonly rootBounds: DOMRectReadOnly | null;
     /*
      * 目标元素 DOM 对象
      */
     readonly target: Element;
     /*
      * 相对于创建文档的时间,当元素可见性发生改变的时间对象
      */
     readonly time: DOMHighResTimeStamp;
 }

在初始化IntersectionObserver对象的时候,需要重点理解rootthreshold的概念。

root

root可以在初始化IntersectionObserver时指定,表示目标元素相对的容器元素,如果未指定则为html根元素。

root和目标元素都被看成是矩形盒子,而rootMargin则是目标元素相对于root的位置偏移量,因为root内部还可能排布的有其他元素或者root本身具有不为0margin属性。

  • rootMargin取正值时,元素在viewport或者祖先元素内部向上或者向左偏移
  • rootMargin取负值时,元素在viewport或者祖先元素内部向下或者向右偏移

(下图引自Building A Dynamic Header With Intersection Observer — Smashing Magazine

1-dynamic-header-intersection-observer.png

threshold

threshold用于指定一个或者多个在0 ~ 1范围内的数字,数字代表的是一个比例,表示元素在root内部可见面积相对于root的百分比。

当目标元素可见面积比例发生改变且在指定的阈值时,就会触发IntersectionObserver初始化时的回调函数,如果不指定,则默认为0,即元素在完全不可见和可见时触发回调函数。

注意:threshold并不能保证回调函数一定在这些指定的阈值到达时执行。

元素是否可见的判断

通过IntersectionObserver我们可以判断元素是否可见的两种情况:

  1. documentviewport内部是否可见,此时元素相对于浏览器窗口或者iframe定位

Edit ancient-sound-l3vbju

  1. 在祖先元素内部是否可见

Edit nervous-tristan-k630cf

IntersectionObserver用例

Image Lazyload

Edit hopeful-poincare-uuhiob

Sticky Header BoxShadow

Edit angry-taussig-e07ysx

Infinite Scroll

Edit intelligent-albattani-yrujgn