全网最全!js四大观察者之IntersectionObserver全部属性与方法详解
我正在参加「掘金·启航计划」
兼容性查询结果如下

IntersectionObserver这个api是用来监听一个dom元素是否出现在另外一个>视口元素的api以前得监听某个元素的滚动,然后不断调用
getBoundingClientRect来判断,非常影响性能
1 简单使用
对于同一个
dom,只能观察一次,不能观察多次
准备一些dom元素
import 'bpm-mobile/build/style/init.css';
.....
<>
{range(6).map(item => <div style={{ height: 50, border: '1px dashed' }} key={item}>{item}</div>)}
<div className='test-box1' style={{ width: 100, height: 100, border: '20px double' }}>test-box1</div>
</>
之后把这些元素放在一共200*200的视口中

2 实例方法
当实例化的时候,第一个参数需要传入一个方法,这个参数是必须的
new IntersectionObserver(function () { })
当实例化完成之后,会返回一个观察者对象
const observer = new ...
这个对象上,存在四个方法
- observe
- unobserve
- disconnect
- takeRecords
2.1 observe
当创建一个交叉观察者之后,我们需要告诉观察者要观察哪个dom元素
observer.observe(document.querySelector('.test-box1') as HTMLElement)
2.2 unobserve
用于告知观察者,不需要观察这个dom元素了
observer.unobserve(document.querySelector('.test-box1') as HTMLElement)
2.3 disconnect
观察元素不只可以添加一个,可以添加多个元素,如下
const doms: HTMLElement[] = []
for (let i = 1; i < 3; i++) {
doms.push(document.querySelector(`.test-box${i}`) as HTMLElement)
observer.observe(doms[i - 1])
}
在某些时候,需要去除所有dom元素的监听,可以使用 disconnect来移除所有元素监听
observer.disconnect()
2.4 takeRecords
在intersectionobserver中用处不大,在 MutationObserver中一定作用
3 callback - entries
当页面第一次的时候,绑定的第一个函数执行一次
entries为 IntersectionObserver实例化时,传入的第一个函数参数的第一个参数回调,这个参数的类型是一个数组(可以监听多个dom),由如下组成
const observer = new IntersectionObserver(
function (entries: IntersectionObserverEntryInit[]) {
console.log(entries[0])
}
)
document.querySelector(`.test-box1`) as HTMLElement
类型定义如下:
interface DOMRectInit {
height?: number;
width?: number;
x?: number;
y?: number;
}
type IntersectionObserverEntryInit = {
target: Element;
boundingClientRect: DOMRectInit;
rootBounds: DOMRectInit;
intersectionRect: DOMRectInit;
intersectionRatio: number;
isIntersecting: boolean;
}[]
3.1 target
指向监听的dom元素

3.2 boundingClientRect
与
getBoundingClientRect获取的参数一致
表示被监听的元素的当前位置和尺寸信息

3.3 rootBounds
与
getBoundingClientRect获取的参数一致
记录视口的 getBoundingClientRect 信息

3.4 intersectionRect
与
getBoundingClientRect获取的参数一致
当监听的元素与与视口交叉的时候,可以根据这个元素,拿到交叉的尺寸

3.5 intersectionRatio
用于获取被监听的元素与视口之间的占用比例,值为 0 - 1
- 当元素部分显示在视口中的时候

- 当元素全部显示在视口中的时候

- 当元素在视口之外的时候

3.6 isIntersecting
如果这个值为true,表示被监听的元素显示在视口中了

如果这个值为false,表示被监听的元素在视口外

4 callback - observer
observer为IntersectionObserver实例化时,传入的第一个函数参数的第二个参数回调
!!!!这个参数建议看完options再回来查看怎么使用
当触发监听之后,回调的第二个参数的值包含如下
interface IntersectionObserver {
// options中设置的root,没设置为null
readonly root: Element | null;
// options中设置的rootMargin,没设置默认为 0px 0px 0px 0px
readonly rootMargin: string;
// options中设置的thresholds,没设置默认为 [0]
readonly thresholds: ReadonlyArray<number>;
// 与创建的实例中存在的回调方法一致
disconnect(): void;
observe(target: Element): void;
takeRecords(): IntersectionObserverEntry[];
unobserve(target: Element): void;
}
5 options
在初始化观察者的时候,第二个参数,可以传递一个对象 options
new(callback: IntersectionObserverCallback, options?: IntersectionObserverInit): IntersectionObserver;
类型定义如下
interface IntersectionObserverInit {
root?: Element | null;
rootMargin?: string;
threshold?: number | number[];
}
5.1 root
单独设置这个参数 “没什么用处”,只是作为一个标识符,用于告知自己,目前正在监听的视口元素是哪一个
比如,这个时候,把所有的元素放在一个滚动列表中
<div className='look' style={{ width: 100, height: 100, overflow: 'auto' }}>
{range(6).map(item => <div style={{ height: 50, border: '1px dashed' }} key={item}>{item}</div>)}
<div className='test-box1' style={{ width: 100, height: 100, border: '20px double' }}>test-box1</div>
</div>
然后把这个dom元素的引用放到 root 中
const observer = new IntersectionObserver(
function (entries: IntersectionObserverEntryInit[], observe: IntersectionObserver) {
console.log(observe.root)
},
{
root: document.querySelector('.look') as Element
}
)
然后触发监听的时候,能通过 observe.root 拿到视口的dom元素

如果不传这个参数,拿到的就是null,但交叉监听不受影响

5.2 rootMargin
5.2.1 使用
设置视口的监听访问,格式如下,必须设置四个属性,格式为 上右下左
new IntersectionObserver(
......
{
rootMargin: '0px 0px 50px 0px', // 正确
// rootMargin: '0 0 50px 0', // 错误
// rootMargin: '50px', // 错误
}
)
当设置为下面的属性之后
rootMargin: '10px 20px 50px 30px'
原来的视口监听 和 设置marginRoot 之后的对比

这样,滚动到 -50px 的位置就能触发监听了

5.2.1 rootBounds
当开发者把 rootMargin 设置为如下代码之后
rootMargin: '10px 20px 50px 30px'
当触发监听之后,打印 entrie.rootBounds之后,发现数据变成了视口的尺寸加上了 rootMargin 设置的大小

5.2.3 root
当把这些元素塞在一个 overflow:auto 中的时候,会发现,设置的rootMargin失效了
又变成了未设置 rootMargin 之前的效果,虽然交叉监听没有出问题

并且,打印 rootBounds,发现,获取到的视口的尺寸还是整个视口尺寸,这明显是错误的

还记得root属性吗,上面说到,单独设置root没有什么效果。rootMargin需要借助 root 才能打印正常的值
设置root
new IntersectionObserver(
.....
{
root: document.querySelector('.look') as Element,
rootMargin: '10px 20px 50px 30px'
}
)
这个时候,发现,打印的rootBounds正常了

并且设置的rootMargin也生效了

5.3 threshold
阈值,可传入一个number,也可以传入一个number数组 , 可传的值在 [0 -1] 之间
5.3.1 传入number
当设置为 0.5 的时候
new IntersectionObserver(
......
{
threshold: 0.5
}
)
表示被监听的元素必须出现在视口50%以上,或小于视口50%以下的时候才会触发监听事件
- 小于50%
当小于50%的时候, isIntersecting都为false

- 大于50%

5.3.2 传入number[]
传入的是 [.3, .7]
new IntersectionObserver(
......
{
threshold: [.3, .7]
}
)
概念图像如下
交叉面积不断变大

交叉面积不断变小

5.3.3 root
rootMargin需要root的数据,而threshold只与交叉面积有关,不设置root也没事
转载自:https://juejin.cn/post/7249177497191366711