HTML5观察模式Observer

HTML5观察模式Observer

Tags
HTML5
开发
Published
Published January 2, 2022
Property
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