提示
对于不支持某些新特性的浏览器,一般都可以通过profill进行兼容;https://polyfill.io/v3/
参考文档:https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API
Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。
过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:
过去,相交检测通常要用到事件监听,并且需要频繁调用 Element.getBoundingClientRect()
方法以获取相关元素的边界信息。事件监听和调用 Element.getBoundingClientRect() 都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。
假如有一个无限滚动的网页,开发者使用了一个第三方库来管理整个页面的广告,又用了另外一个库来实现消息盒子和点赞,并且页面有很多动画(译注:动画往往意味着较高的性能消耗)。两个库都有自己的相交检测程序,都运行在主线程里,而网站的开发者对这些库的内部实现知之甚少,所以并未意识到有什么问题。但当用户滚动页面时,这些相交检测程序就会在页面滚动回调函数里不停触发调用,造成性能问题,体验效果让人失望。
Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时 (或者 viewport ),或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。
注意 Intersection Observer API 无法提供重叠的像素个数或者具体哪个像素重叠,他的更常见的使用方式是——当两个元素相交比例在 N% 左右时,触发回调,以执行某些逻辑。
Intersection Observer API 允许你配置一个回调函数,当以下情况发生时会被调用
通常,您需要关注文档最接近的可滚动祖先元素的交集更改,如果元素不是可滚动元素的后代,则默认为设备视窗。如果要观察相对于根 (root) 元素的交集,请指定根 (root) 元素为null。
无论您是使用视口还是其他元素作为根,API 都以相同的方式工作,只要目标元素的可见性发生变化,就会执行您提供的回调函数,以便它与所需的交叉点交叉。
目标 (target) 元素与根 (root) 元素之间的交叉度是交叉比 (intersection ratio)。这是目标 (target) 元素相对于根 (root) 的交集百分比的表示,它的取值在 0.0 和 1.0 之间。
建一个 IntersectionObserver 对象,并传入相应参数和回调用函数,该回调函数将会在目标 (target) 元素和根 (root) 元素的交集大小超过阈值 (threshold) 规定的大小时候被执行。
let options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
let observer = new IntersectionObserver(callback, options);
阈值为 1.0 意味着目标元素完全出现在 root 选项指定的元素中可见时,回调函数将会被执行。
创建一个 observer 后需要给定一个目标元素进行观察。可以同时观察多个目标,上滑、下滑都会触发回调。
let target = document.querySelector('#listItem');
observer.observe(target);
每当目标满足该 IntersectionObserver 指定的 threshold 值,回调被调用。
只要目标满足为 IntersectionObserver 指定的阈值,就会调用回调。回调接收 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
});
};
注册的回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法。