什么是观察者?
在深入了解现代浏览器支持的观察者系列之前,让我们先了解一般的观察者是什么?
观察者是一个观察或注意到某事的程序。观察者有助于观察浏览器中发生的某些活动并做出相应的响应。

我们经常处理用 addEventListener 来添加事件监听用户的各种操作,比如:click, mousedown, drag, wheel 等(完整事件列表参考:https://developer.mozilla.org/zh-CN/docs/Web/Events) 。

现代浏览器提供了 5 种 Observer ,使用这些 Observer ,我们可以观察浏览器中发生的不同类型的活动,并采取必要的行动。例如。我们可以观察到,视频是否显示在视口中并启用自动播放,是否已从父 DOM 元素中添加或删除子元素,是否更改了框元素的大小 / 尺寸等等。

以下是现代浏览器支持的五种不同类型的 Observer 。

  1. Intersection Observer
  2. Mutation Observer
  3. Performance Observer
  4. Resize Observer
  5. ReportingObserver

[

](https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver/observe)
Mutation Observer
用于观察 DOM 元素的变化。观察父节点中子节点的添加或删除、属性值或数据内容的变化等变化是很有用的。在 MutationObserver 之前,DOM 变化事件由 Mutation 事件处理,例如 DOMAttrModified, DOMAttributeNameChanged, DOMNodeInserted.
Mutation Observer 被设计为替代 DOM3 事件规范中定义的突变事件。避免突变事件的实际原因是性能问题跨浏览器支持
这个新 api 的最大受众可能是构建 JS 框架的人,主要是为了解决问题和创建交互。另一个用例是您正在使用操纵 DOM 的框架并且需要有效地对这些修改做出反应(并且没有 setTimeout hack)。
** 浏览器支持:** 它对不同的浏览器都有很好的支持。

一般来说,MutationObserver 的实现需要三个步骤
a) 创建观察者
只需调用其构造函数并传递处理函数和配置选项即可创建。我们可以选择指定我们想要跟踪或观察什么样的变化。

const config = {
  childList: true,
  attributes: true,
  characterData: true,
};
const observer = new MutationObserver(handler);

childList:true,表示观察子节点相关的变化,attributes:true 表示观察属性变化,characterData:true 表示观察目标元素数据内容的变化。

b) 定义目标对象来观察
observer.observe (…) 方法接受应该被观察的目标元素。

const parent = document.querySelector(.parent”);
observer.observe(parent, config);

c) 定义回调处理程序
根据观察者创建过程中使用的配置,只要目标元素发生更改,就会执行回调函数。回调函数由突变记录对象触发,其中包含目标元素中发生的突变类型。

function handler(mutationRecords, observer) {
 mutationRecords.forEach((mutationRecord) => {
 switch(mutationRecord.type) {
 case “childList”: //child node added or removed
 break;
 case “attributes”: // attribute changed
 break;
 default:
 }
 });
}

这里准备了一个示例,每当添加或删除新的子节点以及属性更改时,它都会触发 **MutationObserver ** 的回调处理程序。
示例:

<!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 {
        margin: 10px;
      }
      .button {
        background: purple;
        color: white;
        cursor: pointer;
        display: inline-block;
        font-size: 20px;
        padding: 1.75rem 1rem;
        text-align: center;
        text-decoration: none;
        font-weight: bold;
        margin: 5px;
      }
      .button:hover {
        background: #ad1457;
      }
      .parent {
        border: 1px solid #ccc;
        padding: 10px;
      }
      .child {
        background: #673ab7;
        color: #fff;
        padding: 10px;
        margin-top: 5px;
      }
      .infoBoard {
        padding: 10px;
        border: 1px solid #ccc;
        font-size: 20px;
        padding: 1.75rem 1rem;
        background: #f4f4f4;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <div>
        <div class="button addBtn">Add Child Node</div>
        <div class="button removeBtn">Remove Child Node</div>
        <div class="button changeAttrBtn">Change Attribute Value</div>
      </div>
      <div class="parent" data-count="0">
        <div class="child">Child</div>
      </div>
      <div class="infoBoard"></div>
    </div>
    <script>
      if (!("MutationObserver" in window)) {
        document.body.innerText = "Not supported by your browser";
      }
      const config = {
        childList: true,
        attributes: true,
        characterData: true,
      };
      const observer = new MutationObserver(handler);
      const parent = document.querySelector(".parent");
      observer.observe(parent, config);
      function handler(mutationRecords, observer) {
        console.log("Handle mutation");
        mutationRecords.forEach((mutationRecord) => {
          const info = document.querySelector(".infoBoard");
          switch (mutationRecord.type) {
            case "childList":
              info.innerText =
                "Mutation Observer handler: Child node added or removed";
              break;
            case "attributes":
              info.innerText = `Mutation Observer handler: Parent attribute modified: ${mutationRecord.attributeName} : ${mutationRecord.target.children.length}`;
              break;
            default:
              info.innerText = "";
          }
        });
      }
      document.querySelector(".addBtn").addEventListener("click", () => {
        console.log("Add child");
        const child = document.createElement("div");
        child.className = "child";
        child.innerText = "Child ";
        parent.appendChild(child);
      });
      document.querySelector(".removeBtn").addEventListener("click", () => {
        console.log("Remove child");
        const child = parent.firstChild;
        parent.removeChild(child);
      });
      /* Change attribute value when clicked on change attribute button */
      document.querySelector(".changeAttrBtn").addEventListener("click", () => {
        console.log("Change attribute value");
        parent.setAttribute("data-count", parent.childNodes.length);
      });
    </script>
  </body>
</html>