这是一个实验中的功能
此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。由于该功能对应的标准文档可能被重新修订,所以在未来版本的浏览器中该功能的语法和行为可能随之改变。

Intersection observer API提供了一种方法,可以异步观察目标元素与祖先元素或顶层文件的交集变化。

一直以来,检测元素的可视状态或者两个元素的相对可视状态都不是件容易事,毕竟大部分解决办法并非完全可靠,也极易拖慢整个网站的性能。然而,随着网页发展,对上述检测的需求也随之增加了。多种情况下都需要用到元素交集变化的信息,比如:

  • 当页面滚动时,懒加载图片或其他内容。
  • 实现“可无限滚动”网站,也就是当用户滚动网页时直接加载更多内容,无需翻页。
  • 为计算广告收益,检测其广告元素的曝光情况。
  • 根据用户是否已滚动到相应区域来灵活开始执行任务或动画。

过去,交集检测通常需要涉及到事件监听,以及对每个目标元素执行Element.getBoundingClientRect() 方法以获取所需信息。可是这些代码都在主线程上运行,所以任何一点都可能造成性能问题。当网页遍布这些代码时就显得比较丑陋了。

就拿无限滚动网页来举例。它们通常遍布动画图片,不仅需要使用第三方库来处理页面上的广告,还需要使用自定义库来处理通知箱或其他信息。以上每一项都有自己独立的交集检测,全部在主线程上运行。网站作者甚至可能意识不到这件事,毕竟他使用了多个库,也不可能对个中细节知根知底。当用户滚动页面时,这些元素交集检测方法会持续不断的被滚动事件调用,造成很不好的体验。

Intersection Observer API 会注册一个回调方法,每当期望被监视的元素进入或者退出另外一个元素的时候(或者浏览器的视口)该回调方法将会被执行,或者两个元素的交集部分大小发生变化的时候回调方法也会被执行。通过这种方式,网站将不需要为了监听两个元素的交集变化而在主线程里面做任何操作,并且浏览器可以帮助我们优化和管理两个元素的交集变化。

Intersection Observer API 不能告诉你的一件事情是 (两个元素的)重叠部分的准确像素个数或者重叠的像素属于哪一个元素。然而这个API覆盖最广的最常用的使用方式是"如果两个元素发生的交集部分在N%左右,我需要做处理一些事情(执行回调)"

Intersection observer 概念和用法

Intersection observer 允许你配置一个回调函数,每当target,元素和设备视口或者其他指定元素发生交集的时候该回调函数将会被执行。设备视口或者其他元素我们称它为root element 或者 root。特别是你想要监听(target)元素相对于浏览器视口(root)的交集变化的时候(intersection API初始化的时候 如果设置root参数为null或者不设置则root默认为设备视口)。无论你是使用viewport或者其他element做为root intersection API都会以相同方式工作,每当tartget element在root 中的可见性交集数量发生变化(target元素的位置在root中发生变化)都会执行你提供的回调函数

target element 和 root的相交程度我们用intersection ratio来描述。这个(intersection ratio)是一个关于target element相对于root的交集百分比的表述,它的取值在0.0和1.0之间。

创建一个 intersection observer

创建一个 IntersectionObserver对象并 传入相应参数和回调用函数,该回调函数将会在target 元素和root的交集大小超过threshold规定的大小时候被执行。

var options = {
    root: document.querySelector('#scrollArea'), 
    rootMargin: '0px', 
    threshold: 1.0
}
var callback = function(entries, observer) { 
    /* Content excerpted, show below */ 
};
var observer = new IntersectionObserver(callback, options);

当 threshold 达到 1.0 的含义是当target元素完全出现在root元素(root参数指定的元素)里面的时候回调函数将会被执行。

Intersection observer options

IntersectionObserver的参数说明

root
如果root参数指定为null或者不指定的时候默认使用浏览器视口做为root。
rootMargin  
root元素的外边距。类似于css中的 margin 属性,比如 "10px 20px 30px 40px" (top, right, bottom, left)。如果有指定root参数,则rootMargin也可以使用百分比来取值。该属性值是用作root元素和target发生交集时候的计算交集的区域范围,使用该属性可以控制root元素每一边的收缩或者扩张。默认值为0。
threshold
可以是单一的number也可以是number数组,target元素和root元素相交程度达到该值的时候IntersectionObserver注册的回调函数将会被执行。如果你只是想要探测当target元素的在root元素中的可见性超过50%的时候,你可以指定该属性值为0.5。如果你想要target元素在root元素的可见程度每多25%就执行一次回调,那么你可以指定一个数组[0, 0.25, 0.5, 0.75, 1]。默认值是0(意味着只要有一个target像素出现在root元素中,回调函数将会被执行)。该值为1.0含义是当target完全出现在root元素中时候 回调才会被执行。

Targeting an element to be observed

为每个观察者配置一个目标

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

每当目标满足该IntersectionObserver指定的threshold值,回调被调用。

var callback = function(entries, observer) { 
    entries.forEach(entry => {
        entry.time;               // a DOMHightResTimeStamp indicating when the intersection occurred.
        entry.rootBounds;         // a DOMRectReadOnly for the intersection observer's root.
        entry.boundingClientRect; // a DOMRectReadOnly for the intersection observer's target.
        entry.intersectionRect;   // a DOMRectReadOnly for the visible portion of the intersection observer's target.
        entry.intersectionRatio;  // the number for the ratio of the intersectionRect to the boundingClientRect.
        entry.target;             // the Element whose intersection with the intersection root changed.
    });
};

请留意,你注册的回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法。

How intersection is calculated -- 交集的计算

所有区域均被Intersection Observer API 当做一个矩形看待。如果元素是不规则的图形也将会被看成一个包含元素所有区域的最小矩形,相似的,如果元素发生的交集部分不是一个矩形,那么也会被看作是一个包含他所有交集区域的最小矩形。

这个有助于理解IntersectionObserverEntry 的属性,IntersectionObserverEntry用于描述target和root的交集。

The intersection root and root margin

在我们开始跟踪target元素和容器元素之前,我们要先知道什么是容器(root)元素。容器元素又称为intersection root, 或 root element.这个既可以是target元素祖先元素也可以是指定null则使用浏览器视口做为容器(root)。

用作描述intersection root 元素边界的矩形可以使用root margin来调整矩形大小,即rootMargin属性,在我们创建IntersectionObserver对象的时候使用。rootMargin的属性值将会做为margin偏移值添加到intersection root 元素的对应的margin位置,并最终形成root 元素的矩形边界。

Thresholds

IntersectionObserver API并不会每次在元素的交集发生变化的时候都会执行回调。相反它使用了thresholds参数。当你创建一个observer的时候,你可以提供一个或者多个number类型的数值用来表示target元素在root元素的可见程序的百分比,然后,API的回调函数只会在元素达到thresholds规定的阈值时才会执行。

例如,当你想要在target在root元素中中的可见性每超过25%或者减少25%的时候都通知一次。你可以在创建observer的时候指定thresholds属性值为[0, 0.25, 0.5, 0.75, 1],你可以通过检测在每次交集发生变化的时候的都会传递回调函数的参数"IntersectionObserverEntry.isIntersecting"的属性值来判断target元素在root元素中的可见性是否发生变化。如果isIntersecting 是 true,target元素的至少已经达到thresholds属性值当中规定的其中一个阈值,如果是false,target元素不再给定的阈值范围内可见。

为了让我们感受下thresholds是如何工作的,尝试滚动以下的例子,每一个colored box 的四个边角都会展示自身在root元素中的可见程度百分比,所以在你滚动root的时候你将会看到四个边角的数值一直在发生变化。每一个box都有不同的thresholds:

  • 第一个box的thresholds属性值 [0.00, 0.01, 0.02, ..., 0.99, 1.00].
  • 第二个box只有唯一的值 [0.5].
  • 第三个box thresholds按10%从0递增(0%, 10%, 20%, etc.).
  • 最后一个box [0, 0.25, 0.5, 0.75, 1.0]

Interfaces

IntersectionObserver
Provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport. The ancestor or viewport is referred to as the root.
IntersectionObserverEntry
Provides information about the intersection of a particular target with the observers root element at a particular time. Instances of this interface cannot be created, but a list of them is returned by IntersectionObserver.takeRecords().

Specifications

Specification Status Comment
Intersection Observer Working Draft Initial definition.

浏览器兼容性

We're converting our compatibility data into a machine-readable JSON format. This compatibility table still uses the old format, because we haven't yet converted the data it contains. Find out how you can help!

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support

51.0

? ? ? ?
Feature Android Android Webview Firefox Mobile (Gecko) Firefox OS IE Mobile Opera Mobile Safari Mobile Chrome for Android
Basic support 未实现 51.0 ? ? ? ? ? 51.0

更多参考

文档标签和贡献者

此页面的贡献者: cyrilluce, iahu, varcat, joshchiucn, a372210777, DearVikki, xrr2016
最后编辑者: cyrilluce,