Intersection Observer 详解

简介: 在一些场景下,尤其是在移动端,我们需要判断元素是否进入用户视口(viewport),来进行以下处理,例如无限滚动,当底部元素进入视口之后加载下一页内容。

在一些场景下,尤其是在移动端,我们需要判断元素是否进入用户视口(viewport),来进行以下处理,例如无限滚动,当底部元素进入视口之后加载下一页内容。

通常的做法是监听 scroll 事件,然后调用目标元素的 getBoundingClientRect 方法获取元素位置,判断其是否出现在视口之内,缺点是 scroll 触发非常密集,导致计算量很大。

现在新出的 IntersectionObserver 可以自动观察元素是否可见,截止 2022.07.31 其浏览器的适配情况如下

1682518262(1).png

那么接下来我们就来看一下它的具体语法。


构造器


var observer = new IntersectionObserver(callback[, options]);

构造器返回一个 IntersectionObserver 对象

如果指定rootMargin则会检查其是否符合语法规定,检查阈值以确保全部在 0.0 到 1.0 之间,并且阈值列表会按升序排列。如果阈值列表为空,则默认为一个 [0.0] 的数组。

  • callback:当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数,进入视口和离开视口都会触发
  • options:用来配置 observer 实例的对象,如果 options 未指定,observer 实例默认使用文档视口作为 root,并且没有 margin,阈值为 0%
  • root:监听元素的祖先元素 Element 对象,其边界盒将被视作视口。
  • rootMargin:一个在计算交叉值时添加至根的边界盒中的一组偏移量,默认值是"0px 0px 0px 0px"。
  • threshold:规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组 0.0 到 1.0 之间的数组。

1682518298(1).png

当rootMargin不存在时会抛出语法错误,SyntaxError

一个或多个阈值超出了 0.0 到 1.0 的范围时会抛出返回错误,RangeError


方法


同其他 Observer API 一样,IntersectionObserver 也提供了那几个方法,下面我们将按照这套模板来演示各个 API 的效果

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    #container {
      width: 200px;
      height: 300px;
      overflow: auto;
      background-color: #fff;
    }
    .content {
      height: 200px;
    }
    .content:nth-of-type(odd) {
      background-color: pink;
    }
    .content:nth-of-type(even) {
      background-color: blue;
    }
    #footer {
      background-color: red;
      height: 30px;
    }
  </style>
</head>
<body>
  <div id="container">
    <div class="content"></div>
    <div class="content"></div>
    <div id="footer">到底了</div>
  </div>
  <script src="./index.js"></script>
</body>
</html>
复制代码

observe

添加监听元素,当元素出现或消失在视口中时触发 observer 的回调

const footer = document.getElementById('footer');
const observer = new IntersectionObserver((entries) => {
  console.log(entries);
})
observer.observe(footer)
复制代码

在页面上进行滚动时就能看到效果,如果是显示触发的,isIntersecting 值为 true,如果是隐藏触发的,isIntersecting 的值就是 false

1682518352(1).png

unobserve

observer.unobserve(document.getElementById('footer'))
复制代码

取消观察后滚动元素不再触发回调

1682518378(1).png

takeRecords

返回一个IntersectionObserverEntry对象,每个对象的目标都包含每次相交的信息,可以通过调用此地地观察者的方法显式或隐式调用。

Note: 如果使用回调来监视这些更改,则无需调用此方法。调用此方法会清除挂起的相交状态列表,因此不会运行回调。

const intersectionObserverEntries = intersectionObserver.takeRecords();
复制代码

disconnect

停止当前 observer 对所有元素的监听

observer.disconnect()
复制代码


应用场景


懒加载

一些元素,如图片等,在 DOM 渲染阶段可以不加载,当图片出现在视口时开始加载

// 省略 20 个 img 元素
const observer = new IntersectionObserver(
  (changes) => {
    console.log(changes);
    changes.forEach(({ target, isIntersecting }) => {
      if (!target.src && isIntersecting)
        target.src = target.dataset.src
    });
  }
);
Array.from(document.querySelectorAll('img'))
  .forEach((item) => {
    observer.observe(item);
  });
复制代码

我们通过触发监听器的元素解构获得 target 元素和 isIntersecting 属性,通过元素是否存在 src 属性和是否进入视口来处理图片

最终效果如下

1682518406(1).png

无限滚动

当靠近列表底部时,开始加载下一页内容

const container = document.getElementById('container'); // ul元素
const footer = document.getElementById('footer'); // div
let id = 1 // 递增 id
const observer = new IntersectionObserver((entries) => {
  entries.forEach(({ isIntersecting }) => {
    if (isIntersecting) {
      footer.innerText = 'loading...'
      setTimeout(() => {
        const newList = []
        for (let i = 0; i < 20; i++) {
          const li = document.createElement('li')
          li.appendChild(document.createTextNode(id++))
          container.appendChild(li)
        }
        footer.innerText = '到底了'
      }, 2000)
    }
  })
})
observer.observe(footer)
复制代码

监听 footer 元素,当 footer 元素出现在视口中时开始请求数据,这里用定时器进行模拟,将新的元素添加到无需列表中。

效果如下:

1682518432(1).png

IntersectionObserverEntry

  • time:可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
  • target:被观察的目标元素,是一个 DOM 节点对象
  • rootBounds:根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回null
  • boundingClientRect:目标元素的矩形区域的信息
  • intersectionRect:目标元素与视口(或根元素)的交叉区域的信息
  • intersectionRatio:目标元素的可见比例,即intersectionRectboundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
  • isIntersecting:布尔值,表示目标元素的相交状态变化,true 表示从不相交到相交(即出现在视口),false 反之


相关文章
|
移动开发 Android开发 iOS开发
uniapp开发H5及app监听返回事件(以及监听不到的处理方法)
uniapp开发H5及app监听返回事件(以及监听不到的处理方法)
3389 0
|
存储 缓存 JavaScript
【Uniapp 专栏】深入剖析 Uniapp 的运行机制原理
【5月更文挑战第12天】Uniapp是一款基于Vue.js的跨平台前端框架,通过抽象不同平台的差异,实现一套代码多平台运行,提升开发效率。其架构包括视图层(基于Vue.js组件)、逻辑层(JavaScript处理业务逻辑)和数据层(管理应用数据)。运行时,Uniapp会根据目标平台转换代码并适配。关键技术包括Web标准、原生插件和运行时环境。优化措施包含代码分包、数据懒加载和缓存机制。借助HBuilderX等工具,开发者能高效地进行开发和测试。Uniapp的运行机制融合多种技术,为跨平台应用开发提供便利。
1407 2
【Uniapp 专栏】深入剖析 Uniapp 的运行机制原理
|
iOS开发 开发者 Windows
uniapp云打包ios应用证书的获取方法,生成指南
打包用到的一共两个文件,一个是p12格式的私钥证书,一个是证书profile文件。其中生成p12证书的时候,按照官网的教程,是需要MAC电脑来协助做的,主要是生成一些csr文件和导出p12证书等。其实这些步骤也可以借助一些其他的工具来实现,不一定使用mac电脑,用windows电脑也可以创建。
1549 0
|
缓存 JavaScript 前端开发
基于虚拟滚动的大型文档性能优化方案
基于虚拟滚动的大型文档性能优化方案旨在提高长列表或长文档的加载和滚动性能。虚拟滚动通过只渲染视口(用户可见区域)附近的元素来减少内存占用和渲染时间,而非一次性加载所有内容。
|
JSON JavaScript 定位技术
Echarts 绘制地图(中国、省市、区县),保姆级教程!
Echarts 绘制地图(中国、省市、区县),保姆级教程!
27099 154
|
人工智能 算法 搜索推荐
单纯接入第三方模型就无需算法备案了么?
随着人工智能的发展,企业接入第三方模型提升业务能力的现象日益普遍,但算法备案问题引发诸多讨论。根据相关法规,无论使用自研或第三方模型,只要涉及向中国境内公众提供算法推荐服务,企业均需履行备案义务。这不仅因为服务性质未变,风险依然存在,也符合监管要求。备案内容涵盖模型基本信息、算法优化目标等,且需动态管理。未备案可能面临法律和运营风险。建议企业提前规划、合规管理和积极沟通,确保合法合规运营。
|
存储 JavaScript API
Vue3实现图片懒加载及自定义懒加载指令
Vue3实现图片懒加载及自定义懒加载指令
1622 1
|
JSON 小程序 前端开发
小程序长列表优化实践
小程序如何实现长列表优化呢
小程序长列表优化实践