全网最全!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