背景
以往,判断某个元素是否进入了“视口”,传统的方法都是监听“scroll”事件,调用某元素的getBoundingClientRec()方法获取位置信息来判断是否在视口内,但是由于scroll事件密集发生,计算量大容易导致性能问题。
浏览器原生提供的IntersectionObserver API ,可以“自动观察”元素是否是可见,由于可见的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"。
传统判断元素进入视口的实现方式
调用元素的getBoundingClientRec()方法获取位置信息
实现思路:
- 获取浏览器的视口宽高
- window.innerHeight || document.documentElement.clientHeight
- window.innerWidth || document.documentElement.clientWidth
- 获取元素的位置信息 element.getBoundingClientRect()
- 元素在视窗内会满足以下条件:
当top >=0(即往上滚动),top要 <= 视窗的高度; 当 top < 0(即往下滚动),bottom >= 0
当left >=0(即往右滚动),left要 <= 视窗的宽度;当 left < 0(即往左边滚动),right >= 0
const isVisible = (dom) => {
const viewHeight = window.innerHeight || document.documentElement.clientHeight
const viewWidth = window.innerWidth || document.documentElement.clientWidth
const { top, left, right, bottom} = dom
// 当元素在垂直方向上都有在可视窗区域内,则判断在水平方向上元素是否在可视窗区域内
if((top >= 0 && top <= viewHeight) || (top < 0 && bottom >= 0)) {
return left >= 0 ? left <= viewWidth : right >= 0
}
// 如果没进入到上述条件 意味着元素不在视窗内
return false
}
通过intersection observer来实现监听
Intersection Observer是一种异步观察目标元素的方法,因此他接受一个回调函数callback作为参数,第二个参数是option可配置项
具体的参数介绍可查看文档:Intersection Observer - Web API 接口参考 | MDN
const box = document.getElementById('box')
// 创建观察实例
const io = new IntersectionObserver((entries) => {
// 接收的参数entries是一个数组,每个成员都是一个IntersectionObserverEntry对象
console.log(entries)
})
// 传入目标元素开始观察
io.observe(box)
注意:观察者中的回调函数一般会触发两次。一次是目标元素开始进入视口时触发,另一次是目标元素完全离开视口时触发
回调函数中每个成员(IntersectionObserverEntry 对象) 里重要属性介绍:
boundingClientRect: 包含目标元素的位置信息和宽 (bottom/left/right/top/height/width/x/y)
intersectionRatio: 指目标元素出现在可视区域的比例
intersectionRect: 指目标元素和可视区域的相交区域
isIntersecting: 返回一个布尔值,如果目标元素出现在可视区域则为true 如果从可视区域完全消失否则为false
rootBounds: 根元素矩形区域的位置信息和大小 一般默认是视口
target: 被观察的目标元素
从以上属性可以得知,我们可以拿到属性“isIntersecting” 来判断目标元素是否在可视区域内了
const io = new IntersectionObserver((entries) => {
entries.forEach(entry => {
console.log(entry.isIntersecting ? '显示' : '消失')
})
})
实现给出现的目标元素增加样式效果
思路:
- 观察对象中isIntersecting属性来给在视窗的目标元素切换类名
- 需要延时触发回调函数,达到视觉上切换类名展现样式的过渡效果
想要控制回调函数的触发时机,需要使用第二个参数配置对象,设置" threshold "
new IntersectionObserver((entries) => {
// threshold 设置了0.8 意味着目标元素出现视窗80%时才会触发
console.log('目标元素出现在视窗80%了')
}, { threshold: 0.8 })
下面实现的案例:获取所有的li标签元素,给li标签出现在视窗50%的元素添加一个active类名
// 获取所有li标签
const items = document.querySelectorAll('li')
// 创建观察实例
const io = new IntersectionObserver(entries => {
entries.forEach(entry => {
const { target, isIntersecting } = entry
// 给目标元素添加类名
target.classList.toggle('active', isIntersecting)
})
}, {
threshold: 0.5. // 表示目标元素出现在视窗的50%才会触发回调
})
// 遍历获取的元素 观察所有元素
items.forEach(element => io.observe(element)
观察元素进入视口的应用场景
最常见的场景就是图片的懒加载和列表的无限滚动,以往的做法都是通过监听scroll去实现。
具体的如何使用IntersectionObserver去实现这两种场景,后续再发布文章。