Skip to content

IntersectionObserver

📌 什么是 IntersectionObserver?

IntersectionObserver 提供了一种异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法.

常见用途包括:

  • 图片或组件的懒加载

  • 实现无限滚动

  • 页面滚动时触发动画

  • 可见性埋点(如 PV/曝光统计)

🔧 基本使用方式

js
const observer = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('元素进入视口:', entry.target);
      // 在此触发加载或动画逻辑
      observer.unobserve(entry.target); // 可选,观察一次后取消
    }
  });
}, {
  root: null,          // 默认为浏览器视口
  rootMargin: '0px',   // root 边距,可扩大或缩小可视区域
  threshold: 0.1       // 可见比例阈值,0.1 表示 10% 可见时触发
});

const target = document.querySelector('#myElement');
observer.observe(target);

🧠 参数说明

  • root:设置用来检测可见性的“容器”,null 表示浏览器视口。
  • rootMargin:设置根元素的 margin,可以用来提前触发。
  • threshold:阈值,可以是 0~1 之间的数或数组,表示多少比例可见时触发回调。

上面代码中,IntersectionObserver 是浏览器原生提供的构造函数,接受两个参数:callback 是可见性变化时的回调函数,callback 一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。option 是配置对象(该参数可选)

构造函数的返回值是一个观察器实例。实例的 observe 方法可以指定观察哪个 DOM 节点。

js
// 开始观察
io.observe(document.getElementById('example'))

// 停止观察
io.unobserve(element)

// 关闭观察器
io.disconnect()

实例:无限滚动

demo

html
<!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>Intersection Observer 实现一个无限滚动</title>
    <style>
      div {
        width: 80%;
        margin: 0 10%;
        display: flex;
        flex-direction: column;
      }

      .item {
        height: 200px;
        width: 200px;
        margin: 20px auto;
        text-align: center;
        line-height: 200px;
        font-size: 60px;
      }
      .item:nth-child(odd) {
        background-color: red;
      }
      .item:nth-child(even) {
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div id="box"></div>
    <script>
      var io = new IntersectionObserver(
        (entries, observer) => {
          entries.forEach((i) => {
            console.log("i.boundingClientRect", i.boundingClientRect)
            if (i.boundingClientRect.bottom < 0) {
              unobserve(i.target) //i.target被观察的目标元素,是一个 DOM 节点对象
              add(10)
              console.log('===========')
            }
          })
        },
        {
          /* Using default options. Details below */
        }
      )
      function unobserve(target) {
        // 停止观察
        io.unobserve(target)
      }
      let time = 0
      function add(num = 10) {
        const box = document.getElementById('box')

        let list = []
        let i = 1
        while (i <= num) {
          let item = document.createElement('div')
          item.className = 'item'
          item.innerText = time * 10 + i
          list.push(item)
          i++
          if (i === 5) {
            io.observe(item)
          }
        }
        box.append(...list)
        time++
        console.log(time)
      }
      add()
    </script>
  </body>
</html>

惰性加载(lazy load)

有时,我们希望某些静态资源(比如图片),只有用户向下滚动,它们进入视口时才加载,这样可以节省带宽,提高网页性能。这就叫做"惰性加载"。

有了 IntersectionObserver API,实现起来就很容易了。

设置 option 的 rootMargin (偏移量)

html
<img data-src="real.jpg" alt="图片" />

<script>
const imgs = document.querySelectorAll('img[data-src]');
const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      obs.unobserve(img);
    }
  });
});

imgs.forEach(img => observer.observe(img));
</script>