IntersectionObserver-无限滚动
IntersectionObserver-无限滚动
日常需求中, 经常会遇到列表需要上拉加载下一页数据, 也就是一个无限滚动的效果, 过去我们的实现方案无外乎监听scroll
的变化, 通过scrollTop
, scrollHeight
和 clientHeight
来判断滚动的高度, 从而决定是否加载下一页的数据,但这一方案额外增加了JavaScript
的计算量,造成了性能问题,实不能称为一个完美的解决方案。但是今天我们有了一个完美的方案: IntersectionObserver
:
IntersectionObserver
接口 (从属于Intersection Observer API) 提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。
1. API
1. IntersectionObserver
对象
IntersectionObserver
:一个挂载在window
上的全局对象,使用时需要先进行实例化,根据传入的配置的options
参数来决定什么时候触发回调函数callback
。
1 | const io = new IntersectionObserver(callback, options); |
2. 实例方法
实例化IntersectionObserver
对象生成的io
实例先不要扔,还有很重要的作用。
在实例化之后,io
还需要调用一个监听方法来实现对某个DOM
元素的监听,否则,这个实例io
是没有作用的,最多占点内存。
1 | // 监听id为footer的元素 |
3. options
属性对象
1 | // options对象 |
4. IntersectionObserverEntry
对象
1 | const io = new IntersectionObserver(function(entries) { |
2. 注意点
1. 兼容性
以下是该API
的兼容性:
虽然Chrome
从51版本就开始支持了这个新API
,但是我们日常开发中不单单开发只针对Chrome
平台的应用,所以兼容性还是要做的。
2. 异步执行
IntersectionObserver
这个观察期是异步执行的,不随着目标元素的滚动同步触发。而且其异步执行的优先级非常低,只有线程空闲下来才会执行。所以当我们需要实现一些实时性非常高的效果时(如元素吸顶效果),不建议采用这种方案。
3. 实例
1. 无限滚动(Infinite scroll
)
鉴于IntersectionObserver
的兼容性问题,我们在使用此API
之前应该加入一些垫片,来兼容一些比较旧的机型。
安装垫片
npm
方式1
yarn add intersection-observer
script
方式 先把intersection-observer.js下载到本地对应的目录,当然也可以放在cdn上
引入垫片
在项目入口处或者当前文件顶部引入:
npm
方式1
import 'intersection-observer';
script
方式1
<script src="path/to/intersection-observer.js"></script>
放心使用
1
2
3
4
5
6
7
8
9const io = new IntersectionObserver((entries) => {
if (entries[0].intersectionRatio > 0) {
loadMore();
}
}, {
threshold: [0.5],
});
io.observe(document.getElementById('footer'));
2. 惰性加载(lazy load
)
有时,我们希望某些静态资源先不要加载,而是等到它们进入可视区域内再开始加载,这样一方面减少宽带的压力,另一方面也提升了网页性能,这种加载方式就叫做惰性加载。
针对对应的静态资源(如
img
),先不要设置src
,而是先给一个data-src
属性用来保存每个img
对应的静态资源路径:1
2
3<img src="placeholder.png" data-src="img-1.jpg">
<img src="placeholder.png" data-src="img-2.jpg">
<img src="placeholder.png" data-src="img-3.jpg">监听对应的静态资源
DOM
,当它们进入可视区域内时把data-src
上的静态资源路径赋值给相应的src
属性,来进行加载渲染。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function query(selector) {
return Array.from(document.querySelectorAll(selector));
}
var io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
entry.target.src = entry.target.dataset.src;
observer.unobserve(entry.target);
});
}
);
query('.lazy-loaded').forEach(function (item) {
observer.observe(item);
});
3. 视频自动播放
我们希望实现一个当视频区域进入可视区域内时再开始播放视频:
引入这个视频
1
<video src="foo.mp4" controls=""></video>
监听视频
DOM
,自动播放或者暂停1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16let video = document.querySelector('video');
let isPaused = false;
let observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.intersectionRatio != 1 && !video.paused) {
video.pause();
isPaused = true;
} else if (isPaused) {
video.play();
isPaused=false;
}
});
}, {threshold: 1});
observer.observe(video);