IntersectionObserver MDN Api
IntersectionObserver API详解
过去,要检测一个元素是否可见或者两个元素是否相交并不容易,比如实现图片懒加载、内容无限滚动等功能时,都需要通过getBoundingClientRect()写大量的逻辑计算或者依靠scroll事件监听等性能很差方式来实现。
现在,依靠IntersectionObserver我们能非常便捷且高效的实现上述功能。
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 之间。
// 创建实例
const observer = new IntersectionObserver(callback, option);
// 开始观察element1
observer.observe(element1);
// 开始观察element2
observer.observe(element2);
// 停止观察
observer.unobserve(element);
// 关闭观察器
observer.disconnect();
IntersectionObserver是浏览器原生提供的构造函数,接受两个参数:callback是可见性变化时的回调函数,option是配置对象(该参数可选)。
构造函数的返回值是一个观察器实例。实例的observe方法可以指定观察哪个 DOM 节点,如果需要观察多个DOM节点可以多次添加observe方法。
当以下情况发生时会调用回调函数:
callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象。举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。
每个IntersectionObserverEntry对象属性含义如下:
IntersectionObserver构造函数的第二个参数是一个配置对象。它可以设置以下属性。
IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。
注册的回调函数将会在主线程中被执行,所以该函数执行速度要尽可能的快。如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>IntersectionObservertitle>
head>
<body style="font-size: 24px;text-align: center">
<div id="container">div>
<div id="loadMore">加载中...div>
body>
<script>
const container = document.querySelector('#container');
const loadMore = document.querySelector('#loadMore');
let index = 0;
const loadItems = (count) => {
[...Array(count).keys()].forEach((key) => {
const p = document.createElement('P');
p.innerHTML = `${key + index}`;
container.appendChild(p)
})
index += count;
}
const observer = new IntersectionObserver((entries) => {
entries.forEach(({ isIntersecting }) => {
if (isIntersecting) {
loadItems(20);
}
})
});
observer.observe(loadMore)
script>
html>