特别声明:本文来源于@Jitendra Kasaudhan的《Different Types Of Observers Supported By Modern Browsers》一文。
在深入了解现代浏览器支持的观察者之前,让我们试着先了解什么观察者是什么。
观察者是什么
观察者(Observer)是一个观察或注意事物的程序。观察者可以观察浏览器中发生的某些活动并做出相应的响应。观察者类似于狗,观察某些活动,并提醒我们发生了一些不寻常的事情。一旦我们为某些活动获取到狗发出的警告时,我们有责任采取相应的行动。
使用观察者我们可以观察浏览器中发生的不同类型的活动(Activity),并采取必要的行动。比如,可以观察一个视频是否在视窗中显示,并启用自动播放;比如从父DOM元素中添加或删除子元素;比如一个盒了元素的大小尺寸发生变化等等。
以下是现代浏览器支持的四种不同类型的观察者:
- Intersection Observer
- Mutation Observer
- Resize Observer
- Performance Observer
IntersectionObserver
它用于观察两个HTML DOM元素之间的交集。当DOM进入或离开可见的视窗(Visible Viewport)时,在DOM中观察一个元素是很有用的。下面描述了IntersectionObserver
的一些用例。
- 当一个元素在视窗中可见时,延迟加载图像或其他资淅
- 识别广告的可见性并计算广告收入
- 实现网站的无限滚动。当用户向下滚动页面时,不必要浏览不同的页面
- 当一个元素在视窗中时,加载和自动播放、视频或动画
目前,Firefox和Chrome支持IntersectionObserver
,但对于不支持的浏览器,可以找到对应的Polyfill。
使用IntersectionObserver
API主要需要三个步骤:
- 创建观察者
- 定义要观察的目标对象
- 定义回调事件
创建观察者
const options = {
root: document.querySelector('.scrollContainer'),
rootMargin: '0px',
threshold: [0.3, 0.5, 0.8, 1]
}
const observer = new IntersectionObserver(handler, options)
[0.3]
的阈值意味着,当目标元素在根元素指定的元素内可见30%
时,调用处理函数。上面意味着当元素被30%
、50%
、80%
和100%
可见时,将调用处理程序、回调函数。
定义要观察的目标对象
我们可以定义多个目标对象来观察。正如前面的例子所提到的,狗应该知道在提醒每个人之前应该观察什么。
任何目标元素都可以通过调用.observer(target)
方法来观察。
const target = document.querySelector(“.targetBox”);
observer.observe(target);
定义回调事件
这是当一个人注意到某件不寻常的事情发生时的反应。当目标元素与根元素通过阈值相交时,就会触发回调函数。
function handler (entries, observer) {
entries.forEach(entry => {
// 每个entry描述一个观察到的目标元素的交集变化
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
我已经准备了一个Demo,它使用IntersectionObserver
来改变目标框的颜色。当用户滚动时,在可见区域内看到它。
MutationObserver
MutationObserver
用于观察DOM元素的变化。可以观察到在父节点中添加或删除子节点、属性值或数据内容等变化。在MutationObserver
之前,DOM更改事件由Mutation
事件处理,比如DOMAttrModified
、DOMAttributeNameChanged
和DOMNodeInserted
。
MutationObserver
被DOM3中定义的Mutation
事件所替代。避免这些突变事件的实际原因是性能问题和跨浏览器支持。
这个新API的最大受众可能是构建JS框架的人员,主要是解决问题和创建交互。另一个用例将是使用框架来操作DOM,并且需要对这些修改进行有效的响应(而不需要设置setTimeout
)。
这个API在不同的浏览器上都得到很好的支持。
同样的,MutationObserver
的实现也需要三个步骤:
创建观察者
它可以通过调用它的构造函数和传递处理函数和配置选项来创建。我们有一个选项来指定我们想要跟踪或观察什么样的变化。
const config = {
childList: true,
attributes: true,
characterData: true
};
const observer = new MutationObserver(handler);
childList: true
,表示观察与子节点相关的变化attributes:true
,表示观察属性更改characterData; true
,表示观察目标元素的数据内容的变化
定义要观察的目标对象
observer.observe(...)
方法接受应该被观察到的目标元素。
const parent = document.querySelector(“.parent”);
observer.observe(parent, config);
定义回调事件
根据在观察者创建过程中使用的配置,当目标元素中发生更改时,会执行回调函数。回调函数是用突变记来对象触发的,该对象包含目标元素中发生的突变类型。
function handler(mutationRecords, observer) {
mutationRecords.forEach((mutationRecord) => {
switch(mutationRecord.type) {
case “childList”: //child node added or removed
break;
case “attributes”: // attribute changed
break;
default:
}
});
}
我已经准备了一个Demo。该Demo将触发MutationObserver
的回调处理程序,每当添加或删除新的节点时,都会使用属性更改。
ResizeObserver
ResizeObserver
允许我们观察DOM元素的内容矩形大小(width
、height
)的变化,并做出相应的响应。它就像给元素添加document.onresize()
或window.resize()
事件。不调整视窗大小而只改变元素的大小,这是很有用的。例如,添加新的子元素,将元素的display
设置为none
或类似可以改变元素大小的的事件。事实上,它只关注内容框(Content Box)变化。比如下面这些行为将会触发ResizeObserver
的行为:
- 当观察到的元素被插入或从DOM中删除时,观察将会触发
- 当观察到的元素
display
值为none
时,观察都会触发 - 观察不会对未替换的内联元素(non-replaced inline element)触发
- 观察不会由CSS的
transform
触发 - 如果元素有显示,而且元素大小不是
0,0
,观察将会触发
ResizeObserver
通知内容框的大小,如下图所示:
不幸的是,到目前为止仅有Chrome64+支持它,大多数浏览器都还不支持它。
同样的,ResizeObserver
的使用也分为三步:
创建观察者
可以通过调用它的构造函数和传递处理函数来创建它。
const observer = new ResizeObserver(handler);
定义要观察的目标对象
定义目标对象,其大小的变化应该被观察到。
const child = document.querySelector(“.child”);
observer.observe(child);
定义回调事件
function handler (entries) {
entries.forEach((entry) => {
const size = entry.target.contentRect;
console.log(`Element’s size: width: ${size.width} , height: ${size.height}`);
});
}
比如下面这个Demo:
有关于
ResizeObserver
更详细的介绍,可以点击这里进行了解。
PerformanceObserver
它主要用于观察性能时间轴(Performance Timeline),并在浏览器记录时通知新的性能条目。它可以用来度量浏览器和Node.js应用程序中某些性能指标。在浏览器中,我们可以使用Window
对象作为window.PerformanceObserver
,在Node.js应用程序中需要perf_hooks
获得性能对象。比如,const {performance} = require('perf_hooks')
。它在下列情况下是有用的。
- 测量请求(Request)和响应(Response)之间的处理时间。(浏览器)
- 在从数据库中检索数据时计算持续时间。(Node.js应用程序)
- 抽象精确的时间信息,使用Paint Timing API,比如First Paint或First Contentful Paint时间
- 使用“User Timing API”、"Navigation Timing API"、“Network Information API”、“Resource Timing API”和“Paint Timing API”访问性能指标
执行PerformanceObserver
需要三个步骤:
创建观察者
可以通过调用它的构造函数和传递处理函数来创建它。
const observer = new PerformanceObserver(logger);
定义要观察的目标对象
observer.observe(...)
方法接受可以观察到的有效的入口类型。这些输入类型可能属于各种性能API,比如User tming或Navigation Timing API。有效的entryType
值:
"mark”
: [USER-TIMING]"measure"
: [USER-TIMING]"navigation"
: [NAVIGATION-TIMING-2]"resource"
: [RESOURCE-TIMING]
代码:
// subscribe to User-Timing events
const config = {
entryTypes: [“mark”, “measure”]
};
observer.observe(config);
定义回调函数事件
当应用程序中使用观察到的事件时,会触发回调处理程序。
function getDataFromServer() {
performance.mark(“startWork”); // see [USER-TIMING]
doWork(); // Some developer code
performance.mark(“endWork”);
performance.measure(“start to end”, “startWork”, “endWork”);
const measure = performance.getEntriesByName(‘start to end’)[0];
}
function logger(list, observer) {
const entries = list.getEntries();
entries.forEach((entry) => {
console.log(“Name: “ + entry.name +
“, Type: “ + entry.entryType +
“, Start: “ + entry.startTime +
“, Duration: “ + entry.duration + “\n”);
});
}
比如下面这个Demo:
你可以随意使用一个PerformanceObserver
API。当一个用户点击一个按钮时,它将标志着开始和结束的瞬间,并测量在3000ms
之后的持续时间。可以在控制台中查看输出结果。
@Irina Shestak在2018年Asia的JSConf上就分享了有关于PerformanceObservers
相关的主题,比如PerformanceObserver
API、NodeJs的perf_hooks
API。
如果你对这些观察者有任何想法、建议或相关改进,欢迎与我们一起分享。
扩展阅读
- Detect DOM changes with Mutation Observers
- IntersectionObserver’s Coming into View
- Performance Observer: Efficient Access to Performance Data
- Observing Intersection Observers
- Lazy loading images using Intersection Observer
如需转载,烦请注明出处:https://www.w3cplus.com/javascript/different-types-of-observers-supported-by-modern-browsers.html