网页开发中我们经常要处理用户交互,我们会用 addEventListener 添加事件监听器来监听各种用户操作,比如 click、mousedown、mousemove、input 等,这些都是由用户直接触发的事件。
那么对于一些不是由用户直接触发的事件呢?比如元素从不可见到可见、元素大小的改变、元素的属性和子节点的修改等,这类事件如何监听呢?
浏览器提供了 5 种 Observer 来监听这些变动:MutationObserver、IntersectionObserver、PerformanceObserver、ResizeObserver、ReportingObserver。
IntersectionObserver
IntersectionObserver 可以监听一个元素和可视区域相交部分的比例,然后在可视比例达到某个阈值的时候触发回调。
<div id="box1">BOX111</div>
<div id="box2">BOX222</div>
#box1,#box2 {
width: 100px;
height: 100px;
background: blue;
color: #fff;
position: relative;
}
#box1 {
top: 500px;
}
#box2 {
top: 800px;
}
const intersectionObserver = new IntersectionObserver(
function (entries) {
console.log('info:');
entries.forEach(item => {
console.log(item.target, item.intersectionRatio)
})
}, {
//可以看到元素 box1 和 box2 在可视范围达到一半(0.5)和全部(1)的时候分别触发了回调。
threshold: [0.5, 1]
});
intersectionObserver.observe( document.querySelector('#box1'));
intersectionObserver.observe( document.querySelector('#box2'));
我们在做一些数据采集的时候,希望知道某个元素是否是可见的,什么时候可见的,就可以用这个 api 来监听,还有做图片的懒加载的时候,可以当可视比例达到某个比例再触发加载。
MutationObserver
监听一个普通 JS 对象的变化,我们会用 Object.defineProperty 或者 Proxy。MutationObserver 可以监听对元素的属性的修改、对它的子节点的增删改。
<div id="box"><button>光</button></div>
#box {
width: 100px;
height: 100px;
background: blue;
position: relative;
}
//我们定时对它做下修改
setTimeout(() => {
box.style.background = 'red';
},2000);
setTimeout(() => {
const dom = document.createElement('button');
dom.textContent = '东东东';
box.appendChild(dom);
},3000);
setTimeout(() => {
document.querySelectorAll('button')[0].remove();
},5000);
然后监听它的变化
const mutationObserver = new MutationObserver((mutationsList) => {
console.log(mutationsList)
});
mutationObserver.observe(box, {
attributes: true,
childList: true
});
应用:如文章水印被人通过 devtools 去掉了,那么就可以通过 MutationObserver 监听这个变化,然后重新加上,让水印去不掉。
ResizeObserver
窗口我们可以用 addEventListener 监听 resize 事件,那元素呢?元素可以用 ResizeObserver 监听大小的改变,当 width、height 被修改时会触发回调。
<div id="box"></div>
#box {
width: 100px;
height: 100px;
background: blue;
}
const box = document.querySelector('#box');
setTimeout(() => {
box.style.width = '200px';
}, 3000);
用 ResizeObserver 监听它的变化
const resizeObserver = new ResizeObserver(entries => {
console.log('当前大小', entries)
});
resizeObserver.observe(box);
PerformanceObserver
创建 PerformanceObserver 对象,监听 mark(时间点)、measure(时间段)、resource(资源加载耗时)、paint(首次内容绘制) 这四种记录时间的行为。然后我们用 mark 记录了某个时间点,点击 button 的时候用 measure 记录了某个时间段的数据,还加载了一个图片。当这些记录行为发生的时候,希望能触发回调,在里面可以上报。 详细:www.zhangxinxu.com/wordpress/2…
<button onclick="measureClick()">Measure</button>
<img src="https://p9-passport.byteacctimg.com/img/user-avatar/4e9e751e2b32fb8afbbf559a296ccbf2~300x300.image" />
const performanceObserver = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
console.log(entry); // 上报
})
});
performanceObserver.observe({
entryTypes: ['paint','resource', 'mark', 'measure']
});
// 比如 performance 可以用 mark 方法记录某个时间点:
performance.mark('registered-observer');
function measureClick() {
// 用 measure 方法记录某个时间段:后两个个参数时间点,不传代表从开始到现在
performance.measure('button clicked');
//performance.mark 可触发 mark 事件
performance.mark('start')
var now = new Date();
while (new Date() - now < 40) { }
performance.mark('end')
console.error(performance.now());
// performance.measure 可触发 measure 事件
performance.measure('taskA_mark', 'start', 'end');
}
ReportingObserver
ReportingObserver 是一个浏览器 API,它可以用来监听来自浏览器的各种报告信息。这些信息可能包括:弃用的 API 使用、浏览器的干预、CSS 特性使用情况、等其他类型的报告。
API介绍:ReportingObserver(callback):创建新的实例,传入报告事件发生时的回调函数。observe(options):开始观察指定类型的报告事件,传入配置对象,指定types。disconnect():停止观察,断开与所有报告事件的关联。
常见报告事件类型:deprecation:废弃API的事件。intervention:浏览器干预的事件。crash:浏览器崩溃的事件。error:一般的错误事件。
// 创建一个 ReportingObserver:
const observer = new ReportingObserver((reports, observer) => {
for (const report of reports) {
console.log(report.type, report.url, report.body);
}
});
// 开始监听:
observer.observe({ types: ["deprecation", "intervention","crash","error"] });
注:不太好模拟,这个没有模拟测试。