判断元素出现在可视区域的方法

我们经常遇到这样的需求——检测一个元素是否可见或者两个元素是否相交,如

  • 图片懒加载——当图片滚动到可见时才进行加载
  • 内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
  • 检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
  • 在用户看见某个区域时执行任务或播放动画

Element.getBoundingClientRect()

返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。

缺点:事件监听和调用都是在主线程上运行,因此频繁触发、调用,造成浏览器频繁的重绘和回流,给网站带来相当大的卡顿

Intersection Observer API

window对象上有一个Intersection Observer API,
它会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时 (或者 viewport ),或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理。

用法
1. 创建一个 intersection observer
  • 创建一个 IntersectionObserver 对象,并传入相应参数和回调用函数,该回调函数将会在目标 (target) 元素和根 (root) 元素的交集大小超过阈值 (threshold) 规定的大小时候被执行。
let options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0
}
//回调函数只会在元素达到thresholds 规定的阈值时才会执行。
let observer = new IntersectionObserver(callback, options);
属性说明
root必须是目标元素的父级元素。如果未指定或者为null,则默认为浏览器视窗。
threshold目标 (target) 元素与根 (root) 元素之间的交叉比 (intersection ratio)取值在 0.0 和 1.0 之间。可以是单一的 number 也可以是 number 数组,默认值是 0 (意味着只要有一个 target 像素出现在 root 元素中,回调函数将会被执行)。该值为 1.0 含义是当 target 完全出现在 root 元素中时候 回调才会被执行。
rootMargin用来扩大或者缩小视窗的的大小

若rootMargin: ‘30px 100px 20px’ 如下图所示:
蓝线表示定义的root元素,添加rootMargin属性后,将原视窗增大,虚线就是现在的视窗,所以元素现在也就在视窗里面了。

在这里插入图片描述

let callback =(entries, observer) => {
  entries.forEach(entry => {
 
    // entry.boundingClientRect  返回包含目标元素的边界信息
    //entry.rootBounds 返回根元素的边界信息
    // entry.intersectionRatio.  返回intersectionRect 与 boundingClientRect 的比例值。
    // entry.intersectionRect.  用来描述根和目标元素的相交区域。
    // entry.isIntersecting 
    //返回一个布尔值,如果目标元素与根相交,则返回true. 如果返回false, 变换是从交叉状态到非交叉状态。
    // entry.target 与根出现相交区域改变的元素
  });
};

打印出来如下:
在这里插入图片描述

2. 给定一个目标元素进行观察
let target = document.querySelector('#listItem');
observer.observe(target);
//unobserve()	停止监听特定目标元素

请留意,你注册的回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法

3. 停止监听特定目标元素
observer.unobserve(target);//例如图片懒加载时,加载完后即停止监听该元素
4. IntersectionObserver对象停止全部监听工作
observer.disconnect();
缺点

1. IntersectionObservers中的回调是在主线程中运行的,数据是异步传递的,这意味着我们的回调函数的调用优先级是比较低的,要等到浏览器空闲时。因此使用此api完成类似滚动相关的动画注定失败,因为数据在你使用它的时候已经过时了。

2.api只是告诉我们元素什么时候进入视窗中,但是它没告诉我们
元素是否被页面其他内容挡住了,或者元素的可视display是否被改成了其他效果 (transform, opacity, filter, etc)。对于顶级文档中的元素,可以通过分析 javascript 中的 DOM 或通过 elementFromPoint() 来判断。 但是对于 iframe 中的元素,则束手无策
进阶:Intersection Observer V2 新增isVisible布尔字段,当该字段为true时可以保证元素一定可见,而没有被遮挡,并且它的options中新增两个字段:
trackVisibility:指示观察者是否将跟踪目标可见性的变化
delay:Set a minimum delay between notifications

  1. Should I Use One IntersectionObserver Instance? Or One Per Element?

参考链接:
https://developer.mozilla.org/zh-CN/docs/Web/API/Intersection_Observer_API
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect
https://web.dev/intersectionobserver-v2/
https://developer.chrome.com/blog/intersectionobserver/#what-is-intersectionobserver-not-about
https://www.w3.org/TR/intersection-observer/

https://www.bennadel.com/blog/3946-using-intersectionobserver-and-ngswitch-to-defer-template-bindings-in-angular-11-0-5.htm
https://github.com/w3c/IntersectionObserver/issues/81

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值