HTML5提供了四种特性API用来监听元素、性能等变化,通过这些API可以替代监听scroll、resize事件,减少代码复杂度。
MutationObserver
监视对DOM树所做更改的能力,当监听的dom发生变化时会触发注册的回调函数。
const observer = new MutationObserver(callback);
方法:
observe(targetElement,options)
:开始监听attributes
(Boolean) 属性的变动childList
(Boolean)子节点的变动(指新增,删除或者更改)characterData
(Boolean)节点内容或节点文本的变动。subtree
(Boolean)是否将该观察器应用于该节点的所有后代节点attributeOldValue
(Boolean)观察attributes变动时,是否需要记录变动前的属性值characterDataOldValue
(Boolean)观察characterData变动时,是否需要记录变动前的值attributeFilter
(Array) 需要观察的特定属性(比如['class','src'])
options 参数,注意
childList
,attributes
或者 characterData
三个属性之中,至少有一个必须为 true
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
监听元素的祖先元素,其边界盒将被视作视口,默认为documentrootMargin
一个在计算交叉值时添加至root的边界盒中的一组偏移量,写法类似CSS的marginthreshold
规定了一个监听目标与边界盒交叉区域的比例值,可以是一个具体的数值或是一组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