浏览器的 5 种 Observer

介绍了监听网页元素的变化的5种观察者


对于一些不是由用户直接触发的事件, 比如元素从不可见到可见、元素大小的改变、元素的属性和子节点的修改等,这类事件如何监听呢?

浏览器提供了 5 种 Observer 来监听这些变动:MutationObserver、IntersectionObserver、PerformanceObserver、ResizeObserver、ReportingObserver。

IntersectionObserver

IntersectionObserver:监听一个元素从不可见到可见,从可见到不可见。

IntersectionObserver 可以监听一个元素和可视区域相交部分的比例,然后在可视比例达到某个阈值的时候触发回调。

<style>
    #box1,
    #box2 {
      width: 100px;
      height: 100px;
      background: blue;
      color: #fff;
      position: relative;
    }
 
    #box1 {
      top: 500px;
    }
 
    #box2 {
      top: 800px;
    }
  </style>
<div id="box1">BOX111</div>
<div id="box2">BOX222</div>
<script>
    const intersectionObserver = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        console.info(entry.isIntersecting,entry.target,entry.intersectionRatio)
      });
    },{threshold:[0.5,1]} // 可见比例为0.5 和1触发回调
);
 
    const box1 = document.getElementById('box1');
    const box2 = document.getElementById('box2');
 
    intersectionObserver.observe(box1);
    intersectionObserver.observe(box2);
</script>

MutationObserver

MutationObserver 可以监听对元素的属性的修改、对它的子节点的增删改。

// Select the node that will be observed for mutations
const targetNode = document.getElementById("some-id");
 
// Options for the observer (which mutations to observe)
const config = { attributes: true, childList: true, subtree: true };
 
// Callback function to execute when mutations are observed
const callback = (mutationList, observer) => {
  for (const mutation of mutationList) {
    if (mutation.type === "childList") {
      console.log("A child node has been added or removed.");
    } else if (mutation.type === "attributes") {
      console.log(`The ${mutation.attributeName} attribute was modified.`);
    }
  }
};
 
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
 
// Start observing the target node for configured mutations
observer.observe(targetNode, config);
 
// Later, you can stop observing
observer.disconnect();

ResizeObserver

元素可以用 ResizeObserver 监听大小的改变,当 width、height 被修改时会触发回调。

<style>
  #box {
    width: 100px;
    height: 100px;
    background: blue;
  }
</style>
 
<div id="box"></div>
<script>
  const box = document.querySelector('#box');
 
  setTimeout(() => {
    box.style.width = '200px';
  }, 2000);
  const resizeObserver = new ResizeObserver(entries => {
    console.log(entries)
  })
  resizeObserver.observe(box)
</script>
 

PerformanceObserver

PerformanceObserver() 构造函数使用给定的观察者 callback 生成一个新的 PerformanceObserver 对象。当通过 observe() 方法注册的 条目类型 的 性能条目事件 被记录下来时,调用该观察者回调。

<html>
<body>
  <button onclick="measureClick()">Measure</button>
 
  <img src="https://p9-passport.byteacctimg.com/img/user-avatar/4e9e751e2b32fb8afbbf559a296ccbf2~300x300.image" />
 
  <script>
    const performanceObserver = new PerformanceObserver(list => {
      list.getEntries().forEach(entry => {
        console.log(entry);// 上报
      })
    });
    performanceObserver.observe({entryTypes: ['resource', 'mark', 'measure']});
 
    performance.mark('registered-observer');
 
    function measureClick() {
      performance.measure('button clicked');
    }
  </script>
</body>
</html>
 

创建 PerformanceObserver 对象,监听 mark(时间点)、measure(时间段)、resource(资源加载耗时) 这三种记录时间的行为。

然后我们用 mark 记录了某个时间点,点击 button 的时候用 measure 记录了某个时间段的数据。

当这些记录行为发生的时候,希望能触发回调,在里面可以上报。

ReportingObserver

当浏览器运行到过时(deprecation)的 api 的时候,会在控制台打印一个过时的报告;浏览器还会在一些情况下对网页行为做一些干预(intervention),比如会把占用 cpu 太多的广告的 iframe 删掉。

![[image.png]]

![[image-1.png]]

浏览器提供了 ReportingObserver 的 api 用来监听这些报告的打印,我们可以拿到这些报告然后上传。

const options = {
  types: ["deprecation"],
  buffered: true,
};
 
const observer = new ReportingObserver((reports, observer) => {
  reportBtn.onclick = () => displayReports(reports);
}, options);
 
observer.observe();
 

浏览器提供了这 5 种 Observer:

  • IntersectionObserver:监听元素可见性变化,常用来做元素显示的数据采集、图片的懒加载
  • MutationObserver:监听元素属性和子节点变化,比如可以用来做去不掉的水印
  • ResizeObserver:监听元素大小变化

还有两个与元素无关的:

  • PerformanceObserver:监听 performance 记录的行为,来上报数据
  • ReportingObserver:监听过时的 api、浏览器的一些干预行为的报告,可以让我们更全面的了解网页 app 的运行情况