js监听元素是否出现在可视区域详解(IntersectionObserver)
作者:甩锅工程师
观察者模式监听判断dom元素是否在可视区域内
本项目是使用vue3的写法。
1.IntersectionObserver
IntersectionObserver
可以用来自动监听元素是否进入了设备的可视区域之内,而不需要频繁的计算来做这个判断。由于可见(visible)的本质是,目标元素与视口产生一个交叉区,所以这个 API 叫做"交叉观察器"
const observer = new IntersectionObserver(callback, option);
IntersectionObserver
是浏览器原生提供的构造函数,接受两个参数:
- callback:可见性发现变化时的回调函数
- option:配置对象(可选)。
构造函数的返回值是一个观察器实例。实例一共有4个方法:
- observe:开始监听特定元素
- unobserve:停止监听特定元素
- disconnect:关闭监听工作
- takeRecords:返回所有观察目标的对象数组
1.1 observe 方法
该方法需要接收一个target
参数,值是Element
类型,用来指定被监听的目标元素
// 获取元素 const target = document.getElementById("dom"); // 开始观察 io.observe(target);
1.2 unobserve 方法
该方法需要接收一个target
参数,值是Element
类型,用来指定停止监听的目标元素
// 获取元素 const target = document.getElementById("dom"); // 停止观察 io.unobserve(target);
1.3 disconnect 方法
该方法不需要接收参数,用来关闭观察器
// 关闭观察器 io.disconnect();
// 页面加载时监听元素 onMounted(() => { var demo3 = document.querySelector('document.querySelector(dom)) // 获取元素 var observer = new IntersectionObserver((mutaions)=>{ // 创建IntersectionObserver对象 console.log(mutaions[0].isIntersecting) }) observer.observe(demo3) // 需要监听的元素 }
1.4 takeRecords 方法
该方法不需要接收参数,返回所有被观察的对象,返回值是一个数组
// 获取被观察元素 const observerList = io.takeRecords();
1.5 callback 参数
目标元素的可见性变化时,就会调用观察器的回调函数callback。
callback一般会触发两次。一次是目标元素刚刚进入视口,另一次是完全离开视口。
const io = new IntersectionObserver((changes, observer) => { console.log(changes); console.log(observer); });
1.6 options
threshold:
决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为[0],即交叉比例(intersectionRatio)达到0时触发回调函数。用户可以自定义这个数组。比如,[0, 0.25, 0.5, 0.75, 1]就表示当目标元素 0%、25%、50%、75%、100% 可见时,会触发回调函数。root
: 用于观察的根元素,默认是浏览器的视口,也可以指定具体元素,指定元素的时候用于观察的元素必须是指定元素的子元素rootMargin
: 用来扩大或者缩小视窗的的大小,使用css的定义方法,10px 10px 30px 20px表示top、right、bottom 和 left的值
2. IntersectionObserverEntry 对象
changes数组中的每一项都是一个IntersectionObserverEntry 对象
boundingClientRect
:目标元素的矩形区域的信息- i
ntersectionRatio
:目标元素的可见比例,即intersectionRect占
boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0 intersectionRect
:目标元素与视口(或根元素)的交叉区域的信息isIntersecting
: 布尔值,目标元素与交集观察者的根节点是否相交(常用)isVisible
: 布尔值,目标元素是否可见(该属性还在试验阶段,不建议在生产环境中使用)rootBounds
:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回nulltarget
:被观察的目标元素,是一个 DOM 节点对象(常用)time
:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
3. 是否在可视区域
onMounted(() => { var observer = new IntersectionObserver((entries) => { console.log(111111, entries[0].isIntersecting); dataMap.showMyBox = !entries[0].isIntersecting; //返回true代表在页面可视区域,false代表不在页面可视区域。 }); observer.observe(document.querySelector(dom)); }
// 页面卸载时可解绑 onBeforeUnmount(() => { if (observer) { observer.unobserve(document.querySelector(dom)); //解绑元素 observer.disconnect(); //停止监听 } });
4. 图片懒加载
使用 IntersectionObserver 非常容易实现图片懒加载,首先需要观察懒加载元素,然后等元素进入可视区域后设置图片 src;同时,还可以结合 IntersectionObserver.rootMargin
实现提前加载图片,一般可以设置为 1~2 倍浏览器窗口的视口高度,优化用户体验
/** * @method lazyLoad * @param {NodeList} $imgList 图片元素集合 * @param {number} preloadHeight 预加载高度 */ export function lazyLoad($imgList, preloadHeight = 1000) { const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { // 目标元素出现在 root 可视区,返回 true const $target = entry.target const src = $target.getAttribute('lazyload') if (src) { $target.setAttribute('src', src) // 真正加载图片 } observer.unobserve($target) // 解除观察 } }) }, { rootMargin: `0px 0px ${preloadHeight}px 0px`, }) Array.prototype.forEach.call($imgList, ($item) => { if ($item.getAttribute('src')) return // 过滤已经加载过的图片 observer.observe($item) // 开始观察 }) }
使用方法:
// 图片元素设置 lazyload 属性 <img lazyload="图片链接" alt="图片说明"> // 观察图片元素 lazyLoad(document.querySelectorAll("[lazyload]"))
5. 元素吸顶、吸底
如果页面结构比较简单可以直接使用 css 粘性布局。
IntersectionObserver 实现元素固定思路也很简单,首先需要给固定元素包一层父元素,父元素指定高度占位,防止固定元素吸附时页面抖动,然后观察父元素的可视状态变化,当父元素即将离开可视区域时改变固定元素的样式。
/** * @method fixBanner * @param {HTMLElement} $observeEle 观察元素 * @param {HTMLElement} $fixEle 固定定位元素 */ export function fixBanner($observeEle, $fixEle) { const $ele = $fixEle const observer = new IntersectionObserver((entries) => { entries.forEach((entry) => { if (entry.isIntersecting) { $ele.style.cssText = '' } else { $ele.style.cssText = 'position: fixed; top: 0; left: 0' } }) }, { threshold: 1, // threshold 设置为 1 表示目标元素完全可见时触发回调函数 }) observer.observe($observeEle) // 开始观察 }
6. 加载更多
IntersectionObserver 实现加载更多需要在列表后面增加一个尾部元素(比如加载更多动画),当尾部元素进入可视区域就加载更多数据,注意尾部元素一定要一直处于所有列表元素的后面。
提前加载高度不能随意设置,如果设置太大会导致尾部元素一直处于可视状态。
function loadMore() { const observer = new IntersectionObserver( (entries) => { const loadingEntry = entries[0] if (loadingEntry.isIntersecting) { // 请求数据并插入列表 } }, { rootMargin: '0px 0px 600px 0px', // 提前加载高度 }, ) observer.observe(document.querySelector('.mod_loading')) // 观察尾部元素 }
总结
到此这篇关于js监听元素是否出现在可视区域(IntersectionObserver)的文章就介绍到这了,更多相关js监听元素出现可视区域内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!