jtwang7 / JavaScript-Note

JavaScript学习笔记
10 stars 2 forks source link

JS - Intersection Observer #33

Open jtwang7 opened 3 years ago

jtwang7 commented 3 years ago

前言

过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:

过去,相交检测通常要用到事件监听,并且需要频繁调用 Element.getBoundingClientRect() 方法以获取相关元素的边界信息。事件监听和调用 Element.getBoundingClientRect() 都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。 此外,不同第三方库实现自己的相交检测方法,会导致页面在滚动时不断触发各种回调。

假如有一个无限滚动的网页,开发者使用了一个第三方库来管理整个页面的广告,又用了另外一个库来实现消息盒子和点赞,并且页面有很多动画(译注:动画往往意味着较高的性能消耗)。两个库都有自己的相交检测程序,都运行在主线程里,而网站的开发者对这些库的内部实现知之甚少,所以并未意识到有什么问题。但当用户滚动页面时,这些相交检测程序就会在页面滚动回调函数里不停触发调用,造成性能问题,体验效果让人失望。

Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。 Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时(或者 viewport ),或者两个元素的相交部分大小发生变化时,执行注册的回调方法。网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。

注意 Intersection Observer API 无法提供重叠的像素个数或者具体哪个像素重叠,他的更常见的使用方式是——当两个元素相交比例在 N% 左右时,触发回调,以执行某些逻辑。

概念

Intersection Observer API 允许你配置一个回调函数,当以下情况发生时会被调用:

无论您是使用视口还是其他元素作为根,API都以相同的方式工作,只要目标元素的可见性发生变化,就会执行您提供的回调函数,以便它与所需的交叉点交叉。

目标(target)元素与根(root)元素之间的交叉度是交叉比(intersection ratio)。这是目标(target)元素相对于根(root)的交集百分比的表示,它的取值在0.0和1.0之间。

IntersectionObserver

构造器

IntersectionObserver() 创建一个新的 IntersectionObserver 对象,当其监听到目标元素的可见部分穿过了一个或多个阈(thresholds)时,会执行指定的回调函数。

当一个IntersectionObserver对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦IntersectionObserver被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。

属性

  1. root (只读) 所监听对象的具体祖先元素(element)。如果未传入值或值为null,则默认使用顶级文档的视窗。
  2. rootMargin (只读) 计算交叉时添加到根(root)边界盒bounding box (en-US)的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。所有的偏移量均可用像素(pixel)(px)或百分比(percentage)(%)来表达, 默认值为"0px 0px 0px 0px"
  3. thresholds (只读) 一个包含阈值的列表, 按升序排列, 列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知(Notification)来触发注册的回调。默认值为0。

    IntersectionObserver API 并不会每次在元素的交集发生变化的时候都会执行回调。相反它使用了 thresholds 参数。当你创建一个 observer 的时候,你可以提供一个或者多个 number 类型的数值用来表示 target 元素在 root 元素的可见程序的百分比,然后,API的回调函数只会在元素达到 thresholds 规定的阈值时才会执行。例如,当你想要在 target 在 root 元素中中的可见性每超过25%或者减少25%的时候都通知一次。你可以在创建 observer 的时候指定 thresholds 属性值为 [0, 0.25, 0.5, 0.75, 1]

方法

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

IntersectionObserverEntry

IntersectionObserverEntry 接口 (从属于 Intersection Observer API ) 描述了目标元素与其根元素容器在某一特定过渡时刻的交叉状态。 IntersectionObserverEntry 的实例作为 entries 参数被传递到一个 IntersectionObserver() 构造函数所注册的回调函数中; 除了从回调函数参数中获取之外, 这些对象就只能通过调用 IntersectionObserver.takeRecords() 来获取.

属性

属性

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

与交叉状态相关的属性保存在 IntersectionObserverEntry 对象上,因此其实例化的 entry 参数也能访问到这些属性,进一步精确控制回调函数内部的行为逻辑。

用法

Step1. 实例化一个 intersection observer

向 IntersectionObserver 构造函数传入相应配置参数和回调函数,实例化一个 IntersectionObserver 对象。所注册的回调函数将会在目标(target)元素和根(root)元素的交集大小超过阈值(threshold)规定的大小时候被执行。

// 构造器配置项
let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}

let observer = new IntersectionObserver(callback, options);

Step2. 添加观察对象

获得 observer 实例对象后需要给定目标元素(一个或多个)进行观察;

let target = document.querySelector('#listItem');
observer.observe(target);

每当目标满足该 IntersectionObserver 指定的 threshold 值,回调被调用。回调接收 所有被添加到观察队列中的目标元素的 IntersectionObserverEntry 实例对象观察者实例对象

let callback =(entries, observer) => {
  entries.forEach(entry => {
    // Each entry describes an intersection change for one observed target element:
    // entry.boundingClientRect
    // entry.intersectionRatio
    // entry.intersectionRect
    // entry.isIntersecting
    // entry.rootBounds
    // entry.target
    // entry.time
  });
};