0
点赞
收藏
分享

微信扫一扫

IntersectionObserver 监听一个元素的可见比例

IntersectionObserver用于监听一个元素的可见比例(一个DOM元素被另一个DOM元素遮挡百分比)变化。

基本使用

const target = document.getElementById('app');

const options = {
  root: rootTarget, // 相对于某个元素进行遮挡计算
  rootMargin: '0px', // 进行计算的边界范围,通过rootMargin可以实现提前计算或延迟计算(相对于root原本尺寸)的效果
  threshold: 0.5 // 触发callback时的遮挡比例,0.5代表元素被遮挡50%时触发callback。由于浏览器事件循环机制的影响,callback触发时遮挡比例通常不会是精确的50%。
};

const intersectionObserver = new IntersectionObserver((entries, observer) => {
  //和MutationObserver相同,也是产生一个array
  entries.forEach(entry => {
    console.log(entry)
  });
}, options);

intersectionObserver.observe(target);

API介绍

observe & options

observe方法用于启动一个Observer对DOM元素的监听。在创建IntersectionObserver时可以通过传入option改变监听的行为。

const options = {
  root: root, 
  rootMargin: '100px', 
  threshold: 0.7 
};

在上面的配置中,通过配置rootMargin为100px在target距离root元素100px时即可判定为被遮挡,通过threshold设置为0.7,当遮挡比例查过70%时执行callback。

entry

callback第一个param是entry对象构成的array,entry包含了触发callback时DOM的位置信息

//被监听DOM元素的Rect信息
boundingClientRect:  {
  bottom: 208
  height: 200
  left: 8
  right: 208
  top: 8
  width: 200
  x: 8
  y: 8
}
intersectionRatio: 1 //交叉比例
// 被监听元素与Root元素交叉部分矩形的Rect信息。
intersectionRect: {
  bottom: 208,
  height: 200,
  left: 8,
  right: 208,
  top: 8,
  width: 200,
  x: 8,
  y: 8
},
// 是否处于交叉状态
isIntersecting: true,
isVisible: false,
// Root元素的Rect信息
rootBounds:  {
  bottom: 606,
  height: 606,
  left: 0,
  right: 476,
  top: 0,
  width: 476,
  x: 0,
  y: 0
},
// root元素
target: div#target,
time: 49.09999990463257

常见场景

乍一看IntersectionObserver好像没啥用,单这个Api在某些场景下十分好用。

比如我们有一个通过sticky固定在屏幕顶部的header元素,我们希望在触发sticky时给header加一个shadow(很多table都有这样的功能)

一种很常见的做法是监听scroll,当滚动一定距离时加上shadow即可。但是监听scroll本身会早成一定的渲染压力(scroll触发非常频繁),同时如果使用React这样的框架又会造成额外的render,在用户的视角看来更卡了。

此时使用IntersectionObserver就很合适了,因为我们需要监听的只是触发sticky的一瞬间,其他的滚动都是无效的,没必要进行计算。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sticky Header with Shadow on Intersection</title>
  <style>
    body {
      margin: 0;
      padding: 0;
    }

    header {
      height: 80px;
      background-color: #3498db;
      color: white;
      text-align: center;
      line-height: 80px;
      position: sticky;
      top: 0;
      z-index: 100;
    }

    .header-shadow {
      transition: box-shadow 0.3s ease;
    }

    .header-shadow.shadow {
      box-shadow: 0 2px 5px black;
    }

    section {
      height: 1000px;
      background-color: #ecf0f1;
      padding: 20px;
    }
  </style>
</head>
<body>
  <div id="guard"></div>
  <header id="sticky-header" class="header-shadow">Sticky Header</header>

  <section>
    <p>向下滚动触发sticky时展示shadow</p>
  </section>

  <script>
    const header = document.getElementById('sticky-header');
    const section = document.querySelector('section');

    const options = {
      threshold: 1
    };
    //guard滚动到可视区域以外时认为触发了shadow
    const intersectionObserver = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          header.classList.remove('shadow');
        } else {
          header.classList.add('shadow');
        }
      });
    }, options);

    intersectionObserver.observe(document.getElementById('guard'));
  </script>

</body>
</html>

举报

相关推荐

0 条评论