HTML5观察模式Observer

HTML5观察模式Observer

Tags
HTML5
Published
Jan 2, 2022
HTML5提供了四种特性API用来监听元素、性能等变化,通过这些API可以替代监听scroll、resize事件,减少代码复杂度。

MutationObserver

监视对DOM树所做更改的能力,当监听的dom发生变化时会触发注册的回调函数。
const observer = new MutationObserver(callback);
方法:
  • observe(targetElement,options):开始监听
    • options 参数,注意childListattributes 或者 characterData 三个属性之中,至少有一个必须为 true
    • attributes (Boolean) 属性的变动
    • childList (Boolean)子节点的变动(指新增,删除或者更改)
    • characterData (Boolean)节点内容或节点文本的变动。
    • subtree (Boolean)是否将该观察器应用于该节点的所有后代节点
    • attributeOldValue (Boolean)观察attributes变动时,是否需要记录变动前的属性值
    • characterDataOldValue (Boolean)观察characterData变动时,是否需要记录变动前的值
    • attributeFilter (Array) 需要观察的特定属性(比如['class','src'])
  • disconnect(): 取消监听
  • takeRecords():删除队列中未处理的记录并返回为新数组
export default function () {
  useEffect(() => {
    const dom = document.querySelector('#title');
    const observer = new MutationObserver((mutationRecord) => {
      mutationRecord.forEach((mutation) => {
        console.log(mutation.type);
      });
    });
    observer.observe(dom, { childList: true, attributes: true });
    return () => {
      observer.disconnect();
    };
  }, []);

  const onClick = () => {
    const dom = document.querySelector('#title');
    dom.innerHTML = 'Hello World';
    dom.setAttribute('title', 'Hello World');
  };

  return (
    <div>
      <h1 id="title">Hello</h1>
      <button onClick={onClick}>点击</button>
    </div>
  );
}

IntersectionObserver

如果指定rootMargin则会检查其是否符合语法规定,检查阈值以确保全部在0.0到1.0之间,并且阈值列表会按升序排列。如果阈值列表为空,则默认为一个[0.0]的数组。
new IntersectionObserver(callback[, options]);
  • callback 是一个回调函数,里面返回监听目标元素的实时数据组成的数组
  • options
    • root 监听元素的祖先元素,其边界盒将被视作视口,默认为document
    • rootMargin 一个在计算交叉值时添加至root的边界盒中的一组偏移量,写法类似CSS的margin
    • threshold 规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组0.0到1.0之间的数组,若指定值为0.0,则意味着监听元素即使与根有1像素交叉,此元素也会被视为可见. 若指定值为1.0,则意味着整个元素都在可见范围内时才算可见
方法:
  • observe(targetElement):开始监听元素
  • disconnect(): 取消所有元素的监听
  • unobserve(targetElement):取消对某一元素的观察
  • takeRecords():对象数组, 每个对象包含目标元素与根每次的相交信息
实现文章内容小标题进入视图区域后锚点跟随滚动,在线代码
export default function () {
  const [currentHeadingIndex, setCurrentHeadingIndex] = useState(0);
  useEffect(() => {
    const headings = document.querySelector('.content').querySelectorAll('h2');
    const observer = new IntersectionObserver(
      (entries) => {
        const io = entries[0];
        if (io.isIntersecting === true) {
          const index = Array.prototype.indexOf.call(headings, io.target);
          setCurrentHeadingIndex(index);
        }
      },
      { threshold: [1] }
    );
    headings.forEach((node) => observer.observe(node));
    return () => {
      headings.forEach((node) => observer.unobserve(node));
    };
  }, []);

  const onClick = (index) => {
    const headings = document.querySelector('.content').querySelectorAll('h2');
    setCurrentHeadingIndex(index);
    headings[index].scrollIntoView();
  };

  const list = [1, 2, 4, 5, 6, 7, 8];

  return (
    <div className="IntersectionObserver">
      <div className="content">
        {list.map((l) => (
          <h2 key={l}>{l}</h2>
        ))}
      </div>
      <div className="anchor">
        {list.map((l, index) => (
          <a
            key={l}
            onClick={() => onClick(index)}
            className={currentHeadingIndex === index ? 'active' : null}
          >
            {l}
          </a>
        ))}
      </div>
    </div>
  );
}

ResizeObserver

可以监听到元素的内容区域边界框改变,内容区域需要减去内边距padding。
方法:
  • observe(targetElement):开始监听元素
  • unobserve(targetElement):取消对某一元素的观察
  • disconnect(): 取消所有元素的监听
export default function () {
  useEffect(() => {
    const observer = new ResizeObserver((entries) => {
      for (let entry of entries) {
        entry.target.innerHTML = entry.target.style.width;
      }
    });
    const dom = document.querySelector('.ResizeObserver');
    observer.observe(dom);
    setTimeout(() => {
      dom.style.width = '200px';
    }, 5000);
		return () => {
      observer.disconnect();
    };
  }, []);

  return <div className="ResizeObserver"></div>;
}

PerformanceObserver

用于监测性能变化,特点是可以在 Web Worker 中可用
方法:
  • observe(options):开始监听
    • options是个单key对象 {entryTypes:['frame,navigation','resource','mark','measure','paint']}
  • takeRecords():返回当前存储在性能观察器中的性能条目列表,将其清空
  • disconnect(): 取消所有监听
const observer = new PerformanceObserver((list, obj) => {
  const entries = list.getEntries();
  for (const i=0; i < entries.length; i++) {
    // Process "mark" and "frame" events
  }
});
observer.observe({entryTypes: ["mark", "frame"]});
 
参考链接:PerformanceObserver