Skip to content
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>Document</title>
  <style>
    html,
    body {
      height: 500%;
    }

    div {
      width: 200px;
      height: 200px;
      background-color: #ddd;
      margin: 0 auto;
      margin-top: 1000px;
    }

    div img {
      width: 100%;
      height: 100%;
      opacity: 0;
    }
  </style>
</head>

<body>
  <!-- 
    IntersectionObserver:异步观察目标元素与其祖先元素或视口交叉状态的方法。
    优点: 方便 内置节流处理
    缺点: 兼容性不好 适合移动端
  -->

  <div>
    <img src="" alt="" data-img="./demo.png">
  </div>

  <div>
    <img src="" alt="" data-img="./demo2.webp">
  </div>

  <div>
    <img src="" alt="" data-img="./demo3.webp">
  </div>

  <script>
    const containers = document.querySelectorAll('div')
    const html = document.documentElement

    let io = new IntersectionObserver(entries => {
      // 初始化时,触发一次回调,监视的所有元素的isIntersecting: false
      // 之后回调的触发只显示满足条件的元素的信息

      // isIntersecting: 元素是否与视口交叉
      const { isIntersecting, target } = entries[0]
      const img = target.children[0]
      if (isIntersecting && !img.isLoaded) {
        // 如果交叉信息为true并且图片没有加载
        img.src = img.dataset.img
        img.style.opacity = 1
        img.isLoaded = true
        io.unobserve(target) // 取消观察
      }
    }, {
      // 如果传了第二个参数
      threshold: [1]
      // 滚轮下滑
      // 监视的每个元素完全出现在视口时,触发一次回调,isIntersecting: true
      // 监视的每个元素顶部一点点离开视口时,触发一次回调,isIntersecting: false

      // 滚轮上滑
      // 监视的每个元素完全出现在视口时,触发一次回调,isIntersecting: true
      // 监视的每个元素底部一点点离开视口时,触发一次回调,isIntersecting: false


      // threshold: [0.5]
      // 监视的每个元素一半出现在视口时,触发一次回调,isIntersecting: true
      // 监视的每个元素一半离开视口时,触发一次回调,isIntersecting: true

      // threshold: [0] 默认值
      // 滚轮下滑
      // 监视的每个元素顶部一点点出现在视口时,触发一次回调,isIntersecting: true
      // 监视的每个元素完全离开视口时,触发一次回调,isIntersecting: false

      // 滚轮上滑
      // 监视的每个元素底部一点点出现在视口时,触发一次回调,isIntersecting: true
      // 监视的每个元素完全离开视口时,触发一次回调,isIntersecting: false

      // threshold: [0, 0.5, 1] // 只要符合上面三种情况任意之一,都会触发
    })

    containers.forEach(container => {
      // 监视所有container与视口交叉信息
      io.observe(container)
    })

    // 一个小问题,如果查看非首屏的图片后刷新,当前视图的图片不会显示,但isIntersecting
    // 为true, 只有它消失再重新出现才会显示, 但一般刷新后滚动条都会重置到最开始,不会
    // 遇到这个问题,如果非要处理的话
    // let io2 = new IntersectionObserver(entries => {
    //   entries.forEach(entry => { // 找到交叉信息为true的容器
    //     if (entry.isIntersecting) {
    //       const { isIntersecting, target } = entry
    //       const img = target.children[0]
    //       if (isIntersecting && !img.isLoaded) {
    //         img.src = img.dataset.img
    //         img.style.opacity = 1
    //         img.isLoaded = true
    //         io.unobserve(target)
    //       }
    //     }
    //   })
    // }, {
    //   threshold: [1]
    // })
  </script>
</body>

</html>