MutationObserver简单理解与应用

90 阅读1分钟

1.MutationObserver理解

MutationObserver作用是监听一个DOM节点,监听其节点上的属性变化、节点文本变化、子节点变化以及其子代节点树节点上的对应变化。
MutationObserver类在创建实例时就需要传递一个Callback监听回调函数,创建实例后就可以调用实例上的observe方法传入DOM节点实例和监听配置config来开启监听,关闭监听则可调用实例上的disconnect方法。监听配置MutationObserverInit中有7个参数如下所示。    
/** 监听属性过滤数组,不监听数组里的属性 */
attributeFilter?: string[];
/** 是否需要记录属性旧值 */
attributeOldValue?: boolean;
/** 是否需要监听属性变化 */
attributes?: boolean;
/** 是否需要监听文本节点的文本内容变化 */
characterData?: boolean;
/** 是否需要记录文本节点的文本旧值 */
characterDataOldValue?: boolean;
/** 是否需要监听子节点变化 */
childList?: boolean;
/** 是否需要监听子代节点的对应变化 */
subtree?: boolean;

2.MutationObserver应用

使用MutationObserver监听节点的高度变化,如下代码块所示,可以自行输入内容,代码会在内容高度发生变化时提示。
[jcode](https://code.juejin.cn/pen/7353175436530024489)
    import React, { useState, useCallback } from 'react@18';
import { createRoot } from 'react-dom@18/client';

// 简单封装个React hook便于使用
export const useMutationObserver = <T extends HTMLElement>(callBack:(mutationsList:MutationRecord[],observerNode:T) => void,config?:MutationObserverInit) => {

  const ref = React.useRef<T>(null);

  const observer = React.useMemo<MutationObserver>(() => {
    // 当观察到变动时执行的回调函数
    const observerCallback = function (mutationsList:MutationRecord[]) {
      if(ref.current) {
        callBack(mutationsList,ref.current);
      }
    };
    // 创建一个观察器实例并传入回调函数
    return new MutationObserver(observerCallback);
  },[]);

  React.useEffect(() => {
    if(ref.current) {
      observer.observe(ref.current,config || { attributes: true, childList: true, subtree:true, });
      return () => {
        observer.disconnect();
      };
    }
  },[]);

  return ref;

};

const Test = function () {

  // 存储节点的高度信息
  const nodeInfo = React.useMemo<{ height?:number }>(() => ({}),[]);

  const observerRef = useMutationObserver((mutationsList,observerNode) => {
    if(observerNode.clientHeight !== nodeInfo.height) {
      window.alert(`height change: ${observerNode.clientHeight}`);  
      nodeInfo.height = observerNode.clientHeight;
    }
  });

  React.useEffect(() => {
    // 初始化节点高度
    if(observerRef.current) {
      nodeInfo.height = observerRef.current.clientHeight;
    }
  },[]);

  return (
    <div>
      请在下方输入:
      <div ref={observerRef} contentEditable></div>
    </div>
  );
  
};
const app = document.getElementById('app');
const root = createRoot(app!)
root.render(<Test />);