交叉观察 API Intersection Observer(1),食堂大妈看完都学会了的说说

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

console.log(‘???~ 执行了一次callback’);

console.log(‘???~ entries:’, entries);

console.log(‘???~ observer:’, observer);

};

//配置对象

const options = {};

//创建观察器

const myObserver = new InterpObserver(callback, options);

//获取目标元素

const target = doc.querySelector(“.target”)

//开始监听目标元素

myObserver.observe(target);

})(document)

我们把这两个参数打印出来看一下,可以看到,第一个参数是一个数组,每一项都是一个目标元素对应的 InterpObserverEntry对象,第二个参数是观察器实例对象 InterpObserver 。

什么是 InterpObserverEntry 对象

展开 InterpObserverEntry 看一下都有什么。

  • boundingClientRect: 一个对象,包含目标元素的 getBoundingClientRect() 方法的返回值。

  • interpRatio: 一个对象,包含目标元素与根元素交叉区域 getBoundingClientRect() 的返回值。

  • interpRect: 目标元素的可见比例,即 interpRect 占 boundingClientRect 的比例,完全可见时为 1 ,完全不可见时小于等于 0 。

  • isIntersecting: 返回一个布尔值,如果目标元素与根元素相交,则返回 true ,如果 isIntersecting 是 true,则 target 元素至少已经达到 thresholds 属性值当中规定的其中一个阈值,如果是 falsetarget 元素不在给定的阈值范围内可见。

  • isVisible: 这个看字面意思应该是 “是否可见” ,如果要让这个属性生效,那么在使用构造函数生成观察器实例的时候,传入的 options 参数必须配置 trackVisibility 为 true,并且 delay 设置为大于 100 ,否则该属性将永远返回 false 。

  • rootBounds: 一个对象,包含根元素的 getBoundingClientRect() 方法的返回值。

  • target:: 被观察的目标元素,是一个 DOM 节点。在观察者包含多个目标的情况下,这是确定哪个目标元素触发了此相交更改的简便方法。

  • time: 该属性提供从 首次创建观察者 到 触发此交集改变 的时间(以毫秒为单位)。通过这种方式,你可以跟踪观察器达到特定阈值所花费的时间。即使稍后将目标再次滚动到视图中,此属性也会提供新的时间。这可用于跟踪目标元素进入和离开根元素的时间,以及两个阈值触发的间隔时间。

这里再看一下 boundingClientRect ,interpRatio , rootBounds 三个属性展开的内容都有什么。

  • bottom: 元素下边距离页面上边的距离

  • left: 元素左边距离页面左边的距离

  • right: 元素右边距离页面左边的距离

  • top: 元素上边距离页面上边的距离

  • width: 元素的宽

  • height: 元素的高

  • x: 等同于 left,元素左边距离页面左边的距离

  • y: 等同于 top,元素上边距离页面上边的距离

用一张图来展示一下这几个属性,特别需要注意的是 right 和 bottom ,跟我们平时写 css 的 position 那个不一样 。

那么第二个参数 InterpObserver 观察器实例对象都有什么呢

别着急,接着往下看,实例属性部分。

观察器实例属性


上面留了一个坑,回调函数的第二个参数 InterpObserver 观察器实例对象都有什么呢?我们把实例对象打印出来看一下

((doc) => {

//回调函数

const callback = () => {};

//配置对象

const options = {};

//创建观察器

const myObserver = new InterpObserver(callback, options);

//获取目标元素

const target = doc.querySelector(“.target”)

//开始监听目标元素

myObserver.observe(target);

console.log(‘???~ myObserver:’, myObserver);

})(document)

可以看到,我们的观察器实例上面包含如下属性

  • root

  • rootMargin

  • thresholds

  • trackVisibility

  • delay

是不是特别眼熟,没错,就是我们创建观察者实例的时候,传入的 options 对象,只不过 options 对象是可选的,观察器实例的属性就使用我们传入的 options 对象,如果没传就使用默认值,唯一不同的是,options 中 的属性 threshold 是单数,而我们实例获取到的 thresholds 是复数。

值得注意的是,这里的所有属性都是 只读 的,也就是说一旦观察器被创建,则 无法 更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值。

接下来我们就通过代码结合动图演示一下这些属性

((doc) => {

let n = 0

//获取目标元素

const target = doc.querySelector(“.target”)

//获取根元素

const root = doc.querySelector(“.out-container”)

//回调函数

const callback = (entries, observer) => {

n++

console.log(????????~ 执行了 ${n} 次callback);

console.log(‘???~ entries:’, entries);

console.log(‘???~ observer:’, observer);

};

//配置对象

const options = {

root: root,

rootMargin: ‘0px 0px 0px 0px’,

threshold: [0.5],

trackVisibility: true,

delay: 100

};

//创建观察器

const myObserver = new InterpObserver(callback, options);

//开始监听目标元素

myObserver.observe(target);

console.log(‘???~ myObserver:’, myObserver);

})(document)

root这个没什么说的,就是设置指定节点为根元素rootMargin我们把 rootMargin 修改为 '50px 50px 50px 50px',可以看到,我们的目标元素还没有露出来的时候回调函数就已经执行了,也就是说目标元素距离根元素还有 50px 的 margin 时,观察器就认为是发生了交叉。thresholds我们把 threshold 修改为 [0.1, 0.3, 0.5, 0.8, 1],可以看到,回调函数触发了多次,也就是说当交叉区域的百分比,每达到指定的阈值时都会触发一次回调函数。

注意 Interp Observer API 无法提供重叠的像素个数或者具体哪个像素重叠,他的更常见的使用方式是——当两个元素相交比例在 N% 左右时,触发回调,以执行某些逻辑。 – MDN

trackVisibility修改 trackVisibility 为 true ,可以看到, isVisible 属性值为 true 。修改 css 属性 为 opacity: 0,可以看到,虽然我们蓝色小方块并没有出现在视图中,但是回调函数已经执行了,并且 isVisible 属性值为 false 而 isIntersecting 值为 true 。delay回调函数延迟触发,我们修改 delay 为 3000,可以看到 log 是 3000ms 以后才输出的。

观察器实例方法


通过此段代码来演示观察器实例方法,为了方便演示,我添加了几个对应的按钮。

((doc) => {

let n = 0

//获取目标元素

const target1 = doc.querySelector(“.target1”)

const target2 = doc.querySelector(“.target2”)

//添加几个按钮方便操作

const observe = doc.querySelector(“.observe”)

const unobserve = doc.querySelector(“.unobserve”)

const disconnect = doc.querySelector(“.disconnect”)

observe.addEventListener(‘click’, () => myObserver.observe(target1))

unobserve.addEventListener(‘click’, () => myObserver.unobserve(target1))

disconnect.addEventListener(‘click’, () => myObserver.disconnect())

//获取根元素

const root = doc.querySelector(“.out-container”)

//回调函数

const callback = (entries, observer) => {

n++

console.log(????????~ 执行了 ${n} 次callback);

console.log(‘???~ entries:’, entries);

console.log(‘???~ observer:’, observer);

};

//配置对象

const options = {

root: root,

rootMargin: ‘0px 0px 0px 0px’,

threshold: [0.1, 0.2, 0.3, 0.5],

trackVisibility: true,

delay: 100

};

//创建观察器

const myObserver = new InterpObserver(callback, options);

//开始监听目标元素

myObserver.observe(target2);

console.log(‘???~ myObserver:’, myObserver);

})(document)

observe

const myObserver = new InterpObserver(callback, options);

myObserver.observe(target);

接受一个目标元素作为参数。很好理解,当我们创建完观察器实例后,要手动的调用 observe 方法来通知它开始监测目标元素。

可以在同一个观察者对象中配置监听多个目标元素

target2 元素是通过代码自动监测的,而 target1 则是我们在点击了 observe 按钮之后开始监测的。通过动图可以看到,当我单击 observe 按钮后,我们的 entries 数组里面就包含了两条数据,前文中说到,可以通过 target 属性来判断是哪个目标元素。

unobserve

const myObserver = new InterpObserver(callback, options);

myObserver.observe(target);

myObserver.unobserve(target)

复制代码

接收一个目标元素作为参数,当我们不想监听某个元素的时候,需要手动调用 unobserve 方法来停止监听指定目标元素。通过动图可以发现,当我们点击 unobserve 按钮后,由两条数据变成了一条数据,说明 target1 已经不再接受监测了。

disconnect

const myObserver = new InterpObserver(callback, options);

myObserver.disconnect()

当我们不想监测任何一个目标元素时,我们需要手动调用 disconnect 方法停止监听工作。通过动图可以看到,当我们点击 disconnect 按钮后,控制台不再输出 log ,说明监听工作已经停止,可以通过 observe 再次开启监听工作。

takeRecords

返回所有观察目标的 InterpObserverEntry 对象数组,应用场景较少。

当观察到交互动作发生时,回调函数并不会立即执行,而是在空闲时期使用 requestIdleCallback 来异步执行回调函数,但是也提供了同步调用的 takeRecords 方法。

如果异步的回调先执行了,那么当我们调用同步的 takeRecords 方法时会返回空数组。同理,如果已经通过 takeRecords 获取了所有的观察者实例,那么回调函数就不会被执行了。

注意事项

====

构造函数 InterpObserver 配置的回调函数都在哪些情况下被调用?


构造函数 InterpObserver 配置的回调函数,在以下情况发生时可能会被调用

  • 当目标(target)元素与根(root)元素发生交集的时候执行。

  • 两个元素的相交部分大小发生变化时。

  • Observer 第一次监听目标元素的时候。

((doc) => {

//回调函数

const callback = () => {

console.log(‘???~ 执行了一次callback’);

};

//配置对象

const options = {};

//观察器实例

const myObserver = new InterpObserver(callback, options);

//目标元素

const target = doc.querySelector(“#target”)

//开始观察

myObserver.observe(target);

})(document)

可以看到,无论目标元素是否与根元素相交,当我们第一次监听目标元素的时候,回调函数都会触发一次,所以不要直接在回调函数里写逻辑代码,尽量通过 isIntersecting 或者 interpRect 进行判断之后再执行逻辑代码。

页面的可见性如何监测


页面的可见性可以通过document.visibilityState或者document.hidden获得。页面可见性的变化可以通过document.visibilitychange来监听。

可见性和交叉观察


当 css 设置了opacity: 0visibility: hidden 以及 用其他的元素覆盖目标元素 ,都不会影响交叉观察器的监测,也就是都不会影响 isIntersecting 属性的结果,但是会影响 isVisible 属性的结果, 如果元素设置了 display:none 就不会被检测了。当然影响元素可视性的属性不止上述这些,还包括positionmarginclip 等等等等…就靠小伙伴们自行发掘了

交集的计算


所有区域均被 Interp Observer API 当做一个 矩形 看待。如果元素是不规则的图形也将会被看成一个包含元素所有区域的最小矩形,相似的,如果元素发生的交集部分不是一个矩形,那么也会被看作是一个包含他所有交集区域的最小矩形。

我怎么知道目标元素来自视口的上方还是下方


目标元素滚动的方向也是可以判断的,原理是根元素的 entry.rootBounds.y 是固定不变的 ,所以我们只需要计算 entry.boundingClientRect.y 与 entry.rootBounds.y 的大小,当回调函数触发的时候,我们记录下当时的位置,如果 entry.boundingClientRect.y < entry.rootBounds.y,说明是在视口下方,那么当下一次目标元素可见的时候,我们就知道目标元素时来自视口下方的,反之亦然。

let wasAbove = false;

function callback(entries, observer) {

entries.forEach(entry => {

const isAbove = entry.boundingClientRect.y < entry.rootBounds.y;

if (entry.isIntersecting) {

if (wasAbove) {

// Comes from top

}

}

wasAbove = isAbove;

});

}

应用场景

====

介绍完基础知识,总得来几个实例(演示代码采用VUE3.0),当然实际场景要比这复杂的多,如何在自己的工作学习中应用,还是要靠小伙伴们多多开动聪明的大脑~

数据列表无限滚动


v-for=‘item in list’

:key=‘item’>内容区域{{item}}

ref=‘reference’>

我们只需要在底部添加一个参考元素,当参考元素可见时,就向后台请求数据,就可以实现无线滚动的效果了。

图片预加载


内容区域
内容区域

ref=‘header’>

内容区域
  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值