Intersection Observer

W3C 工作草案

关于此文档的更多细节
此版本
https://w3org.cn/TR/2023/WD-intersection-observer-20231018/
最新发布版本
https://w3org.cn/TR/intersection-observer/
编辑草案
https://w3c.github.io/IntersectionObserver/
历史版本
历史
https://w3org.cn/standards/history/intersection-observer/
测试套件
http://w3c-test.org/intersection-observer/
反馈
GitHub
编辑
(Google)
(Mozilla)
(Google)
前任编辑
(Google)

摘要

本规范描述了一种 API,可用于了解 DOM 元素(“目标”)相对于包含元素或顶级视口(“根”)的可见性和位置。该位置信息是异步交付的,对于了解元素的可见性以及实现 DOM 内容的预加载和延迟加载非常有用。

关于本文档

本节描述本文档在发布时的状态。当前的 W3C 出版物列表以及本技术报告的最新修订版可在 https://w3org.cn/TR/ 的 W3C 技术报告索引 中找到。

本文档由 Web 应用工作组发布为工作草案。本文档旨在成为 W3C 推荐标准。

本文档由 Web 应用工作组作为工作草案使用推荐标准流程发布。欢迎对本规范提供反馈和意见。请使用 GitHub 问题。历史讨论可在 public-webapps@w3.org 存档中找到。

以工作草案形式发布并不意味着得到 W3C 及其成员的认可。此为草稿文件,随时可能被更新、替代或废止。引用本文件时不应视为正式发布的文献,而应标注为正在进行的工作。

本文档由在 W3C 专利政策下运作的小组编写。W3C 维护一份与该小组交付成果相关的专利披露公开列表;该页面还包含有关披露专利的说明。任何知悉其认为包含必要权利要求的专利的个人,必须按照 W3C 专利政策第 6 节披露相关信息。

本文档受2023 年 6 月 12 日 W3C 流程文档约束。

1. 引言

Web 传统的计算位置的机制依赖于对 DOM 状态的显式查询,这些查询已知会导致(昂贵的)样式重新计算和布局,并且由于持续轮询这些信息,经常成为显著性能开销的来源。

人们已经形成了一套依赖于这些行为的通用实践,包括(但不限于):

这些用例有几个共同属性:

  1. 它们可以表示为关于单个元素相对于其他元素(或全局视口)状态的被动“查询”。

  2. 它们没有严格的延迟要求;换句话说,这些信息可以异步(例如从另一个线程)交付而不会产生负面影响。

  3. 现有的网络平台功能组合对它们的支持很差,尽管它们被广泛使用,却需要开发者付出极大的努力。

一个明确的非目标是提供关于实际显示内容的像素级精确信息(鉴于滤镜、WebGL 和其他功能,在某些浏览器架构中很难高效获取这些信息)。在所有这些场景中,即使信息有轻微延迟且没有完美的合成结果数据,信息也是有用的。

Intersection Observer API 通过为开发者提供一种新方法,以异步查询元素相对于其他元素或全局视口的位置,解决了上述问题。异步交付消除了对昂贵的 DOM 和样式查询、持续轮询以及使用自定义插件的需求。通过移除对这些方法的需求,它使应用程序能够显著降低其 CPU、GPU 和能源成本。

var observer = new IntersectionObserver(changes => {
  for (const change of changes) {
    console.log(change.time);               // Timestamp when the change occurred
    console.log(change.rootBounds);         // Unclipped area of root
    console.log(change.boundingClientRect); // target.getBoundingClientRect()
    console.log(change.intersectionRect);   // boundingClientRect, clipped by its containing block ancestors, and intersected with rootBounds
    console.log(change.intersectionRatio);  // Ratio of intersectionRect area to boundingClientRect area
    console.log(change.target);             // the Element target
  }
}, {});

// Watch for intersection events on a specific target Element.
observer.observe(target);

// Stop watching for intersection events on a specific target Element.
observer.unobserve(target);

// Stop observing threshold events on all target elements.
observer.disconnect();

2. Intersection Observer (交叉观察器)

Intersection Observer API 使开发者能够了解 目标 (target) DOM 元素相对于交叉根 (intersection root) 的可见性和位置。

2.1. The IntersectionObserverCallback (IntersectionObserverCallback)

callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);

目标交叉根的交叉发生变化时,此回调将按照处理模型被调用。

2.2. The IntersectionObserver interface (IntersectionObserver 接口)

IntersectionObserver 接口可用于观察交叉根与一个或多个目标 Element 之间交叉情况的变化。

IntersectionObserver交叉根 (intersection root) 是其 root 属性的值(如果该属性非 null);否则,它是顶级浏览上下文document 节点,称为 隐式根 (implicit root)

具有非 null rootIntersectionObserver 被称为 显式根观察器 (explicit root observer),它可以观察目标 Element 中任何在包含块链中属于 root 后代的元素。具有 null rootIntersectionObserver 被称为 隐式根观察器 (implicit root observer)隐式根观察器的有效目标包括顶级浏览上下文中的任何 Element,以及任何处于顶级浏览上下文后代浏览上下文列表中任何嵌套浏览上下文中的 Element

在处理隐式根观察器时,API 会区分:其相关设置对象顶级源同源域目标,称为 同源域目标 (same-origin-domain target);以及 跨源域目标 (cross-origin-domain target)显式根观察器的任何目标也都是 同源域目标,因为目标必须与交叉根位于同一个 文档 中。

注意:MutationObserver 中,MutationObserverInit 选项被传递给 observe(),而在 IntersectionObserver 中它们被传递给构造函数。这是因为对于 MutationObserver,每个被观察的 Node 可能有不同的属性过滤集。对于 IntersectionObserver,开发者可以选择使用单个观察器来跟踪多个使用相同选项集的目标;或者他们可以为每个跟踪的目标使用不同的观察器。每个目标rootMarginthreshold 值似乎引入了更多的复杂性,而并未解决额外的用例。如果未来有需求,可以提供每个 observe() 的选项。

[Exposed=Window]
interface IntersectionObserver {
  constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
  readonly attribute (Element or Document)? root;
  readonly attribute DOMString rootMargin;
  readonly attribute DOMString scrollMargin;
  readonly attribute FrozenArray<double> thresholds;
  undefined observe(Element target);
  undefined unobserve(Element target);
  undefined disconnect();
  sequence<IntersectionObserverEntry> takeRecords();
};
new IntersectionObserver(callback, options)

返回执行 初始化新的 IntersectionObserver 算法的结果,并提供 callbackoptions

observe(target)

执行 观察目标元素 算法,提供 thistarget

unobserve(target)

执行 取消观察目标元素 算法,提供 thistarget

注意:MutationObserver 不实现 unobserve()。对于 IntersectionObserverunobserve() 解决了延迟加载用例。在 target 变得可见后,就不再需要对其进行跟踪。如果 disconnect() 所有 targets 然后 observe() 剩余的目标,或者为每个 target 创建一个单独的 IntersectionObserver,工作量会更大。

disconnect()

对于 this 内部 [[ObservationTargets]] 槽中的每个 target

  1. target 内部的 [[RegisteredIntersectionObservers]] 槽中移除其 observer 属性等于 thisIntersectionObserverRegistration 记录。

  2. this 内部的 [[ObservationTargets]] 槽中移除 target

takeRecords()
  1. queuethis 内部 [[QueuedEntries]] 槽的副本。

  2. 清除 this 内部 [[QueuedEntries]] 槽。

  3. 返回 queue

root类型为 (Element or Document),只读,可为空

传递给 IntersectionObserver 构造函数的 root,如果未提供则为 null

rootMargin类型为 DOMString,只读

应用于根交叉矩形的偏移量,有效地扩大或缩小用于计算交叉的框。这些偏移量仅在处理同源域目标时应用;对于跨源域目标,它们将被忽略。

获取时,返回将 [[rootMargin]] 的元素以空格分隔序列化的结果,其中像素长度序列化为数值后跟“px”,百分比序列化为数值后跟“%”。注意,这不能保证与传递给 IntersectionObserver 构造函数的 options.rootMargin 完全相同。如果未向 IntersectionObserver 构造函数传递 rootMargin,则此属性的值为“0px 0px 0px 0px”。

scrollMargin类型为 DOMString,只读

偏移量应用于从交叉根目标路径上的滚动端口 (scrollports),有效地扩大或缩小用于计算交叉的裁剪矩形。

获取时,返回将 [[scrollMargin]] 的元素以空格分隔序列化的结果,其中像素长度序列化为数值后跟“px”,百分比序列化为数值后跟“%”。注意,这不能保证与传递给 IntersectionObserver 构造函数的 options.scrollMargin 完全相同。如果未向 IntersectionObserver 构造函数传递 scrollMargin,则此属性的值为“0px 0px 0px 0px”。

thresholds类型为 FrozenArray<double>,只读

阈值列表,按数值增加顺序排列,其中每个阈值是观察目标交叉面积与边界框面积之比。当目标的任何阈值被跨越时,就会为该目标生成通知。如果未向 IntersectionObserver 构造函数提供 options.threshold,或者序列为空,则此属性的值将为 [0]。

如果 Element 的计算样式具有导致其内容被裁剪到元素内边距边缘溢出属性,则该元素被定义为具有 内容裁剪 (content clip)

IntersectionObserver根交叉矩形 (root intersection rectangle) 是我们将用于与目标进行检查的矩形。

如果 IntersectionObserver 是一个隐式根观察器
它将被视为根是顶级浏览上下文document,根据 document 的以下规则。
如果交叉根是一个 document
它是 document视口大小(注意,此处理步骤仅在 document 完全激活时才能到达)。
否则,如果交叉根具有内容裁剪
它是该元素的内边距区域
否则,
它是获取交叉根边界框的结果。

在计算同源域目标根交叉矩形时,矩形随后会根据 IntersectionObserver[[rootMargin]] 槽中的偏移量进行扩展,方式类似于 CSS 的 margin 属性,四个值分别表示上、右、下和左边缘的偏移量,正长度表示向外偏移。百分比是相对于未扩展矩形的宽度进行解析的。

注意:rootMargin 仅应用于交叉根本身。如果目标 Element 被非交叉根的祖先裁剪,则该裁剪不受 rootMargin 的影响。

若要 应用滚动边距到滚动端口 (apply scroll margin to a scrollport)

在计算同源域目标滚动端口交叉矩形时,矩形会根据 IntersectionObserver[[scrollMargin]] 槽中的偏移量进行扩展,方式类似于 CSS 的 margin 属性,四个值分别表示上、右、下和左边缘的偏移量,正长度表示向外偏移。百分比是相对于未扩展矩形的宽度进行解析的。

这些偏移量仅在处理同源域目标时应用;对于跨源域目标,它们将被忽略。

注意:scrollMargin 会影响所有可滚动祖先(包括交叉根)对目标的裁剪。scrollMarginrootMargin 都会应用于可滚动的交叉根矩形。

注意:根交叉矩形滚动端口交叉矩形不受缩放 (pinch zoom) 的影响,并将报告未经调整的视口,这与缩放的意图一致(即表现得像放大镜,而不会改变布局)。

若要从输入字符串 marginString 解析边距 (parse a margin)(根或滚动),返回 4 个像素长度或百分比的列表,或失败

  1. 解析组件值列表 marginString,将结果存储为 tokens

  2. tokens 中移除所有空格标记。

  3. 如果 tokens 的长度大于 4,则返回失败。

  4. 如果 tokens 中有零个元素,则将 tokens 设置为 ["0px"]。

  5. 替换 tokens 中的每个 token

    • 如果 token绝对长度维度标记,则将其替换为等效的像素长度。

    • 如果 token<percentage> 标记,则将其替换为等效的百分比。

    • 否则,返回失败。

  6. 如果 tokens 中有一个元素,则在该元素后面追加三个相同的元素。否则,如果 tokens 中有两个元素,则在该元素后面追加每个元素的副本。否则,如果 tokens 中有三个元素,则追加第二个元素的副本。

  7. 返回 tokens

2.3. The IntersectionObserverEntry interface (IntersectionObserverEntry 接口)

[Exposed=Window]
interface IntersectionObserverEntry {
  constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

dictionary IntersectionObserverEntryInit {
  required DOMHighResTimeStamp time;
  required DOMRectInit? rootBounds;
  required DOMRectInit boundingClientRect;
  required DOMRectInit intersectionRect;
  required boolean isIntersecting;
  required double intersectionRatio;
  required Element target;
};
boundingClientRect类型为 DOMRectReadOnly,只读

通过获取 target 的边界框而获得的 DOMRectReadOnly

intersectionRect类型为 DOMRectReadOnly,只读

boundingClientRect,与 target 的每个祖先的裁剪矩形(向上但不包含 root)取交集,并与根交叉矩形取交集。该值代表 target 实际在根交叉矩形内可见的部分。

isIntersecting类型为 boolean,只读

如果 targetroot 交叉,则为 true;否则为 false。此标志使得区分以下两种情况成为可能:发出从交叉到不交叉转换信号的 IntersectionObserverEntry;以及发出从不交叉到与零面积交叉矩形交叉转换信号的 IntersectionObserverEntry(这会发生在边缘邻接交叉或 boundingClientRect 面积为零时)。

intersectionRatio类型为 double,只读

如果 boundingClientRect 的面积不为零,则这将是 intersectionRect 面积与 boundingClientRect 面积之比。否则,如果 isIntersecting 为 true,则这将为 1,否则为 0。

rootBounds类型为 DOMRectReadOnly,只读,可为空

对于同源域目标,这将是根交叉矩形。否则,这将为 null。注意,如果目标与交叉根位于不同的浏览上下文中,这将处于与 boundingClientRectintersectionRect 不同的坐标系中。

target类型为 Element,只读

其与交叉根的交叉发生变化的 Element

time类型为 DOMHighResTimeStamp,只读

该属性必须返回一个 DOMHighResTimeStamp,它对应于记录交叉的时间,相对于与生成通知的 IntersectionObserver 实例关联的全局对象的时间原点

2.4. The IntersectionObserverInit dictionary (IntersectionObserverInit 字典)

dictionary IntersectionObserverInit {
  (Element or Document)?  root = null;
  DOMString rootMargin = "0px";
  DOMString scrollMargin = "0px";
  (double or sequence<double>) threshold = 0;
};
root类型为 (Element or Document),可为空,默认为 null

用于交叉的 root。如果未提供,则使用隐式根

rootMargin类型为 DOMString,默认为 "0px"

类似于 CSS margin 属性,这是一个由 1-4 个组件组成的字符串,每个组件要么是绝对长度,要么是百分比。

"5px"                // all margins set to 5px
"5px 10px"           // top & bottom = 5px, right & left = 10px
"-10px 5px 8px"      // top = -10px, right & left = 5px, bottom = 8px
"-10px -5px 5px 8px" // top = -10px, right = -5px, bottom = 5px, left = 8px
scrollMargin类型为 DOMString,默认为 "0px"

类似于 rootMargin,这是一个由 1-4 个组件组成的字符串,每个组件要么是绝对长度,要么是百分比。

有关示例,请参见上文的 rootMargin

threshold类型为 (double or sequence<double>),默认为 0

触发回调的阈值列表。当 intersectionRect 的面积从大于或等于任何阈值变为小于该阈值时(反之亦然),将调用回调。

阈值必须在 [0, 1.0] 的范围内,并表示获取目标边界框所生成的矩形面积的百分比。

注意:0.0 实际上意味着“任何非零像素数”。

3. Processing Model (处理模型)

本节概述了用户代理在实现 Intersection Observer API 时必须采取的步骤。

3.1. Internal Slot Definitions (内部槽位定义)

3.1.1. Document (文档)

每个 document 都有一个 IntersectionObserverTaskQueued 标志,初始化为 false。

3.1.2. Element (元素)

Element 对象有一个内部 [[RegisteredIntersectionObservers]] 槽,初始化为空列表。此列表持有 IntersectionObserverRegistration 记录,这些记录具有一个持有 IntersectionObserverobserver 属性、一个持有 -1 到观察器 thresholds 属性长度(含)之间数字的 previousThresholdIndex 属性,以及一个持有布尔值的 previousIsIntersecting 属性。

3.1.3. IntersectionObserver (交叉观察器)

IntersectionObserver 对象拥有内部 [[QueuedEntries]][[ObservationTargets]] 槽(初始化为空列表),以及由 IntersectionObserver(callback, options) 初始化的内部 [[callback]] 槽。它们还拥有内部 [[rootMargin]][[scrollMargin]] 槽,它们是四个像素长度或百分比的列表。

3.2. Algorithms (算法)

3.2.1. Initialize a new IntersectionObserver (初始化新的交叉观察器)

若要 初始化新的 IntersectionObserver,给定一个 IntersectionObserverCallback callback 和一个 IntersectionObserverInit 字典 options,请执行以下步骤:

  1. this 为一个新的 IntersectionObserver 对象。

  2. this 的内部 [[callback]] 槽设置为 callback

  3. 尝试从 options.rootMargin 解析边距。如果返回一个列表,则将 this 的内部 [[rootMargin]] 槽设置为该列表。否则,抛出一个 SyntaxError 异常。

  4. 尝试从 options.scrollMargin 解析边距。如果返回一个列表,则将 this 的内部 [[scrollMargin]] 槽设置为该列表。否则,抛出一个 SyntaxError 异常。

  5. thresholds 为等于 options.threshold 的列表。

  6. 如果 thresholds 中的任何值小于 0.0 或大于 1.0,则抛出一个 RangeError 异常。

  7. thresholds 按升序排序。

  8. 如果 thresholds 为空,则将 0 追加到 thresholds

  9. thresholds 属性获取器将返回此已排序的 thresholds 列表。

  10. 返回 this

3.2.2. Observe a target Element (观察目标元素)

若要 观察目标元素,给定一个 IntersectionObserver observer 和一个 Element target,执行以下步骤:

  1. 如果 target 存在于 observer 的内部 [[ObservationTargets]] 槽中,则返回。

  2. intersectionObserverRegistration 为一个 IntersectionObserverRegistration 记录,其 observer 属性设置为 observerpreviousThresholdIndex 属性设置为 -1previousIsIntersecting 属性设置为 false

  3. intersectionObserverRegistration 追加到 target 的内部 [[RegisteredIntersectionObservers]] 槽中。

  4. target 添加到 observer 的内部 [[ObservationTargets]] 槽中。

3.2.3. Unobserve a target Element (取消观察目标元素)

若要 取消观察目标元素,给定一个 IntersectionObserver observer 和一个 Element target,执行以下步骤:

  1. target 内部的 [[RegisteredIntersectionObservers]] 槽中移除其 observer 属性等于 thisIntersectionObserverRegistration 记录(如果存在)。

  2. this 内部的 [[ObservationTargets]] 槽中移除 target(如果存在)。

3.2.4. Queue an Intersection Observer Task (队列化交叉观察器任务)

IntersectionObserver 任务源 是一个任务源,用于调度任务以执行 § 3.2.5 通知交叉观察器

若要为 document document 队列化交叉观察器任务,请执行以下步骤:

  1. 如果 documentIntersectionObserverTaskQueued 标志设置为 true,则返回。

  2. documentIntersectionObserverTaskQueued 标志设置为 true。

  3. 在与该 document事件循环关联的 IntersectionObserver 任务源队列化一个任务,以通知交叉观察器

3.2.5. Notify Intersection Observers (通知交叉观察器)

若要为 document document 通知交叉观察器,执行以下步骤:

  1. documentIntersectionObserverTaskQueued 标志设置为 false。

  2. notify list 为根 (root) 位于 document 的 DOM 树中的所有 IntersectionObserver 的列表。

  3. 对于 notify list 中的每个 IntersectionObserver 对象 observer,执行以下步骤:

    1. 如果 observer 的内部 [[QueuedEntries]] 槽为空,则继续。

    2. queueobserver 的内部 [[QueuedEntries]] 槽的副本。

    3. 清除 observer 的内部 [[QueuedEntries]] 槽。

    4. callbackobserver 的内部 [[callback]] 槽的值。

    5. 调用 callback,将 queue 作为第一个参数,将 observer 作为第二个参数,并将 observer 作为回调 this 值。如果抛出异常,则报告异常

3.2.6. Queue an IntersectionObserverEntry (队列化 IntersectionObserverEntry)

若要为 IntersectionObserver observer 队列化 IntersectionObserverEntry,给定 document documentDOMHighResTimeStamp timeDOMRects rootBoundsboundingClientRectintersectionRectisIntersecting 标志;以及 Element target;执行以下步骤:

  1. 构造一个 IntersectionObserverEntry,传入 timerootBoundsboundingClientRectintersectionRectisIntersectingtarget

  2. 将其追加到 observer 的内部 [[QueuedEntries]] 槽中。

  3. document 队列化一个交叉观察器任务

3.2.7. Compute the Intersection of a Target Element and the Root (计算目标元素与根的交叉)

若要 计算 目标 target交叉根 root 之间的 交叉,执行以下步骤:

  1. intersectionRect获取 target 边界框的结果。

  2. containertarget包含块

  3. container 不是 root 时:

    1. 如果 container嵌套浏览上下文document,则通过裁剪到该 document视口来更新 intersectionRect,并更新 container 为该 container浏览上下文容器

    2. intersectionRect 映射到 container 的坐标空间。

    3. 如果 container滚动容器,应用 IntersectionObserver[[scrollMargin]]container 的裁剪矩形,如应用滚动边距到滚动端口中所述。

    4. 如果 container 具有内容裁剪或 css clip-path 属性,则通过应用 container 的裁剪来更新 intersectionRect

    5. 如果 container浏览上下文的根元素,则更新 container 为该浏览上下文document;否则,更新 containercontainer包含块

  4. intersectionRect 映射到 root 的坐标空间。

  5. 通过与根交叉矩形取交集来更新 intersectionRect

  6. intersectionRect 映射到包含 targetdocument视口坐标空间。

  7. 返回 intersectionRect

3.2.8. Run the Update Intersection Observations Steps (执行更新交叉观察步骤)

若要为 Document document 执行更新交叉观察步骤(给定时间戳 time),执行以下步骤:

  1. observer list 为根 (root) 位于 document 的 DOM 树中的所有 IntersectionObserver 的列表。对于顶级浏览上下文,这包括隐式根观察器

  2. 对于 observer list 中的每个 observer

    1. rootBoundsobserver根交叉矩形

    2. 对于 observer 内部 [[ObservationTargets]] 槽中的每个 target(按在每个 target 上调用 observe() 的顺序处理):

        • thresholdIndex 为 0。

        • isIntersecting 为 false。

        • targetRect 为一个 DOMRectReadOnly,其 xywidthheight 设置为 0。

        • intersectionRect 为一个 DOMRectReadOnly,其 xywidthheight 设置为 0。

      1. 如果交叉根不是隐式根,且 target 不与交叉根位于同一个 document 中,则跳至步骤 11。

      2. 如果交叉根是一个 Element,且 target包含块链中不是交叉根的后代,则跳至步骤 11。

      3. targetRect 设置为通过获取 target 边界框而获得的 DOMRectReadOnly

      4. intersectionRect 为对 targetobserver交叉根执行计算交叉算法的结果。

      5. targetAreatargetRect 的面积。

      6. intersectionAreaintersectionRect 的面积。

      7. 如果 targetRectrootBounds 相交或边缘邻接,即使交叉面积为零(因为 rootBoundstargetRect 面积为零),也将 isIntersecting 设置为 true。

      8. 如果 targetArea 不为零,则设 intersectionRatiointersectionArea 除以 targetArea否则,如果 isIntersecting 为 true,则设 intersectionRatio1;如果 isIntersecting 为 false,则设为 0

      9. thresholdIndex 设置为 observer.thresholds 中第一个其值大于 intersectionRatio 的条目的索引;如果 intersectionRatio 大于或等于 observer.thresholds 中的最后一个条目,则设置为 observer.thresholds 的长度。

      10. intersectionObserverRegistrationtarget 内部 [[RegisteredIntersectionObservers]] 槽中 observer 属性等于 observerIntersectionObserverRegistration 记录。

      11. previousThresholdIndexintersectionObserverRegistrationpreviousThresholdIndex 属性。

      12. previousIsIntersectingintersectionObserverRegistrationpreviousIsIntersecting 属性。

      13. 如果 thresholdIndex 不等于 previousThresholdIndex,或者 isIntersecting 不等于 previousIsIntersecting,则队列化一个 IntersectionObserverEntry,传入 observertimerootBoundstargetRectintersectionRectisIntersectingtarget

      14. thresholdIndex 分配给 intersectionObserverRegistrationpreviousThresholdIndex 属性。

      15. isIntersecting 分配给 intersectionObserverRegistrationpreviousIsIntersecting 属性。

3.3. IntersectionObserver Lifetime (IntersectionObserver 生命周期)

IntersectionObserver 将保持存活,直到满足以下两个条件:

IntersectionObserver 将持续观察一个目标,直到调用了该观察者的 unobserve() 方法并传入该目标作为参数,或者调用了观察者的 disconnect() 方法。

3.4. 外部规范集成

3.4.1. HTML 处理模型:事件循环

HTML 事件循环处理模型的“更新渲染”步骤中,存在一个 Intersection Observer 处理步骤作为子步骤。

3.4.2. 待处理的初始 IntersectionObserver 目标

如果存在至少一个满足以下条件的 IntersectionObserver,则称一个 document 拥有 待处理的初始 IntersectionObserver 目标
  1. observerroot 位于该 document 中(对于顶级浏览上下文,这包括隐式根观察者)。
  2. observer 在其 [[ObservationTargets]] 插槽中至少有一个 target,且尚未为该目标排入任何 IntersectionObserverEntry

HTML 事件循环处理模型中,“更新渲染”步骤下的“不必要的渲染”步骤应进行修改,增加一个跳过渲染更新的额外要求:

4. 无障碍考量

本节是非规范性的。

IntersectionObserver 核心规范(本文档)没有已知的无障碍考量。然而,有一些相关的规范和提案利用并引用了此规范,它们可能各自有无障碍方面的考量。特别是 HTML § 2.5.7 延迟加载属性CSS Containment 2 § 4 完全抑制元素内容:content-visibility 属性 的相关规范,可能对 HTML § 6.9 页面内查找HTML § 6.6.3 tabindex 属性 以及 空间导航 产生影响。

5. 隐私与安全

本节是非规范性的。

与此 API 相关的主要隐私担忧涉及它可能向跨源 iframe 上下文中运行的代码提供的信息(即跨源域目标的情况)。具体而言:

需要指出的是,在 IntersectionObserver 出现之前,Web 开发人员曾以非常巧妙(有时甚至是怪异)的方式使用其他 API 来窃取 IntersectionObserver 所提供的信息。从纯粹实用的角度来看,该 API 并没有揭示任何通过其他手段无法获得的信息。

另一个考量是 IntersectionObserver 使用了 DOMHighResTimeStamp,它自身也有隐私和安全方面的考量。不过,IntersectionObserver 不太可能遭受与时间相关的漏洞利用。时间戳最多在每次渲染更新时生成一次(见 § 3.4.1 HTML 处理模型:事件循环),这对于常见的定时攻击来说频率太低了。

6. 国际化

本节是非规范性的。

没有已知的国际化相关问题。

7. 致谢

特别感谢所有贡献者,正是你们的技术投入和建议促成了本规范的改进。

一致性

文档约定

一致性要求通过描述性断言和 RFC 2119 术语相结合来表达。本文档规范性部分中的关键词“MUST”(必须)、“MUST NOT”(不得)、“REQUIRED”(必需)、“SHALL”(应)、“SHALL NOT”(不应)、“SHOULD”(推荐)、“SHOULD NOT”(不推荐)、“RECOMMENDED”(建议)、“MAY”(可以)和“OPTIONAL”(可选)应按照 RFC 2119 中的描述进行解释。然而,为了可读性,这些词在本文档中不以全大写形式出现。

本规范的所有文本均为规范性文本,明确标记为非规范性的部分、示例和注释除外。 [RFC2119]

本规范中的示例均以“例如”一词引入,或者通过 class="example" 与规范性文本隔开,如下所示

这是一个说明性示例。

说明性注释以“Note”一词开头,并使用 class="note" 与规范性文本隔开,如下所示

注意,这是一个说明性注释。

一致性算法

作为算法一部分的祈使语气要求(例如“去除任何前导空格字符”或“返回 false 并中止这些步骤”)应根据引入算法时使用的关键词(“必须”、“应该”、“可以”等)进行解释。

以算法或具体步骤表述的一致性要求可以用任何方式实现,只要最终结果等效即可。特别地,本规范定义的算法旨在易于理解,而不旨在提高性能。鼓励实现者进行优化。

索引

本规范定义的术语

通过引用定义的术语

引用

规范性引用

[CSS-BOX-4]
Elika Etemad. CSS Box Model Module Level 4. 2022年11月3日. WD. URL: https://w3org.cn/TR/css-box-4/
[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS Containment Module Level 2. 2022年9月17日. WD. URL: https://w3org.cn/TR/css-contain-2/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS Overflow Module Level 3. 2023年3月29日. WD. URL: https://w3org.cn/TR/css-overflow-3/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. 2021年12月24日. CR. URL: https://w3org.cn/TR/css-syntax-3/
[CSS-VALUES-3]
Tab Atkins Jr.; Elika Etemad. CSS 值与单位模块第 3 级. 2022年12月1日. CR. URL: https://w3org.cn/TR/css-values-3/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS 值和单位模块第 4 级. 2023 年 4 月 6 日. WD. URL: https://w3org.cn/TR/css-values-4/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM 视图模块. 2016 年 3 月 17 日. WD. URL: https://w3org.cn/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM 标准. Living Standard. URL: https://dom.spec.whatwg.org/
[GEOMETRY-1]
Simon Pieters; Chris Harrelson. Geometry Interfaces Module Level 1. 2018年12月4日. CR. URL: https://w3org.cn/TR/geometry-1/
[HTML]
Anne van Kesteren; et al. HTML 标准. Living Standard. URL: https://html.whatwg.cn/multipage/
[RFC2119]
S. Bradner. RFC 中用于指示要求级别的关键词. 1997年3月. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL 索引

callback IntersectionObserverCallback = undefined (sequence<IntersectionObserverEntry> entries, IntersectionObserver observer);

[Exposed=Window]
interface IntersectionObserver {
  constructor(IntersectionObserverCallback callback, optional IntersectionObserverInit options = {});
  readonly attribute (Element or Document)? root;
  readonly attribute DOMString rootMargin;
  readonly attribute DOMString scrollMargin;
  readonly attribute FrozenArray<double> thresholds;
  undefined observe(Element target);
  undefined unobserve(Element target);
  undefined disconnect();
  sequence<IntersectionObserverEntry> takeRecords();
};

[Exposed=Window]
interface IntersectionObserverEntry {
  constructor(IntersectionObserverEntryInit intersectionObserverEntryInit);
  readonly attribute DOMHighResTimeStamp time;
  readonly attribute DOMRectReadOnly? rootBounds;
  readonly attribute DOMRectReadOnly boundingClientRect;
  readonly attribute DOMRectReadOnly intersectionRect;
  readonly attribute boolean isIntersecting;
  readonly attribute double intersectionRatio;
  readonly attribute Element target;
};

dictionary IntersectionObserverEntryInit {
  required DOMHighResTimeStamp time;
  required DOMRectInit? rootBounds;
  required DOMRectInit boundingClientRect;
  required DOMRectInit intersectionRect;
  required boolean isIntersecting;
  required double intersectionRatio;
  required Element target;
};

dictionary IntersectionObserverInit {
  (Element or Document)?  root = null;
  DOMString rootMargin = "0px";
  DOMString scrollMargin = "0px";
  (double or sequence<double>) threshold = 0;
};

MDN

IntersectionObserver/IntersectionObserver

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android55+iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/disconnect

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/observe

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/root

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/rootMargin

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/takeRecords

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/thresholds

在所有当前引擎中。

Firefox55+Safari12.1+Chrome52+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver/unobserve

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserver

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/boundingClientRect

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/intersectionRatio

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/intersectionRect

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/isIntersecting

在所有当前引擎中。

Firefox55+Safari12.1+Chrome58+
Opera?Edge79+
Edge (Legacy)16+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/rootBounds

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/target

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry/time

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
MDN

IntersectionObserverEntry

在所有当前引擎中。

Firefox55+Safari12.1+Chrome51+
Opera?Edge79+
Edge (Legacy)15+IE
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?