ResizeObserverController
类定义
ResizeObserverController
, 这个是个单例的控制器或者叫调度器。所有的 resizeObserver
归它管理,或者说 ResizeObserverSPI
归它管。它是调度器,它会检测 DOM 上面的变化,通过 MutationObserver
或者 DOMSubtreeModified
事件(如果前者不支持的话), 同时它还订阅了transactionend
事件,它只关心这些 css
属性的变化。
const transitionKeys = [
"top",
"right",
"bottom",
"left",
"width",
"height",
"size",
"weight",
];
当这些事件被促发的时候,它就会迭代内部的 SPI 数组,调用它们的gatherActive()
,
hasActive()
过滤出变化的 SPI,然后对于迭代变化的 SPI,依次调用它们的 broadcastActive()
方法。
/**
* Singleton controller class which handles updates of ResizeObserver instances.
*/
class ResizeObserverController {
// 指示是否已添加DOM侦听器。
connected_ = false;
// 告诉控制器已订阅突变事件
mutationEventsAdded_ = false;
// 保留对MutationObserver实例的引用
mutationsObserver_ = null;
// 连接的观察者列表
observers_ = [];
// 調度器實例
static instance_ = null;
/**
* Creates a new instance of ResizeObserverController.
*
* @private
*/
constructor() {
this.onTransitionEnd_ = this.onTransitionEnd_.bind(this);
this.refresh = throttle(this.refresh.bind(this), REFRESH_DELAY);
}
// 添加侦听器
addObserver(observer) {
// 當不存在時,將observer加入到this.observers_數組中
if (!~this.observers_.indexOf(observer)) {
this.observers_.push(observer);
}
// Add listeners if they haven't been added yet.
if (!this.connected_) {
this.connect_();
}
}
// 从列表中移除指定侦听器
removeObserver(observer) {
const observers = this.observers_;
// 找到索引, 找到大於等於0 ,未找到-1
const index = observers.indexOf(observer);
// 存在時 ~index 都小於0 ,轉為Boolean都為TRUE;不存在時~index 為0 ,轉為Boolean為false
if (~index) {
// 移除索引為index的item
observers.splice(index, 1);
}
// 如果控制器没有连接的观察器,则删除侦听器
if (!observers.length && this.connected_) {
this.disconnect_();
}
}
refresh() {
// 当存在活跃侦听器时,返回TRUE
const changesDetected = this.updateObservers_();
// 如果检测到更改,则继续运行更新,因为将来可能会有由CSS转换引起的更改。
if (changesDetected) {
this.refresh();
}
}
// 更新观察者列表中的每个观察者,并通知他们排队的条目
updateObservers_() {
// Collect observers that have active observations.
const activeObservers = this.observers_.filter((observer) => {
// 執行observer.gatherActive(),活躍的觀察者數組清空後重新存儲獲取的observer,由於後續的observer.broadcastActive()需要使用
// 只返回observer.hasActive()為true的observe
return observer.gatherActive(), observer.hasActive();
});
// 在单独的周期中传递通知,以避免任何观察者之间的冲突,例如当ResizeObserver正在跟踪同
// 一个元素和一个元素的回调其中改变了观察到的目标的内容维度。有时这可能导致其他观察者的通知被阻止。
activeObservers.forEach((observer) => observer.broadcastActive());
return activeObservers.length > 0;
}
connect_() {
//如果在非浏览器环境中运行,或者侦听器已存在,则不执行任何操作
if (!isBrowser || this.connected_) {
return;
}
// 订阅“Transitionend”事件用作的解决方法延迟的转换。通过这种方式,至少可以捕获元素的最终状态。
document.addEventListener("transitionend", this.onTransitionEnd_);
// 监听窗口resize变化
window.addEventListener("resize", this.refresh);
// 當瀏覽器支持MutationObserver時
if (mutationObserverSupported) {
//
this.mutationsObserver_ = new MutationObserver(this.refresh);
this.mutationsObserver_.observe(document, {
attributes: true,
childList: true,
characterData: true,
subtree: true,
});
} else {
document.addEventListener("DOMSubtreeModified", this.refresh);
// 设置已订阅DOMSubtreeModified事件
this.mutationEventsAdded_ = true;
}
// 设置侦听器已添加
this.connected_ = true;
}
/**
* Removes DOM listeners.
*
* @private
* @returns {void}
*/
disconnect_() {
// 如果在非浏览器环境中运行,或者侦听器已被删除,则不执行任何操作
if (!isBrowser || !this.connected_) {
return;
}
document.removeEventListener("transitionend", this.onTransitionEnd_);
window.removeEventListener("resize", this.refresh);
if (this.mutationsObserver_) {
this.mutationsObserver_.disconnect();
}
if (this.mutationEventsAdded_) {
document.removeEventListener("DOMSubtreeModified", this.refresh);
}
// 移除对对MutationObserver实例的引用
this.mutationsObserver_ = null;
//指示已取消订阅DOMSubtreeModified事件
this.mutationEventsAdded_ = false;
this.connected_ = false;
}
// 當transitionKeys有一個item存在css過度行為時返回TRUE
onTransitionEnd_({ propertyName = "" }) {
// 检测过渡是否会影响元素的尺寸
const isReflowProperty = transitionKeys.some((key) => {
return !!~propertyName.indexOf(key);
});
if (isReflowProperty) {
this.refresh();
}
}
/**
* Returns instance of the ResizeObserverController.
*
* @returns {ResizeObserverController}
*/
static getInstance() {
if (!this.instance_) {
this.instance_ = new ResizeObserverController();
}
return this.instance_;
}
}
addObserver
方法
按位非运算时,任何数字x
的运算结果都是 -(x + 1)
。例如,~-5
运算结果为 4
。
Array.prototype.indexOf()
方法返回数组中第一次出现给定元素的下标,如果不存在则返回 -1。
存在的時候 [x].indexOf(x)
返回的值是大於等於 0, ~[x].indexOf(x)
返回永遠小於 0
不存在的時候[x].indexOf(z)
返回-1,~[x].indexOf(z)
返回 0
按位非運算例子
console.log(~1);
// Expected output: -2
console.log(~2);
// Expected output: -3
console.log(~0);
// Expected output: -1
console.log(~-1);
// Expected output: 0
逻辑非(!,逻辑连接取反)运算符将真值或假值转换为对应的相反值,经常用于布尔(逻辑)值。当与非布尔值使用时,如果其操作数可以转化为 true,则返回 false,否则返回 true。
console.log(!-2);
// Expected output: false
console.log(!0);
// Expected output: true
数值转为布尔类型示例
Boolean(0); //false
Boolean(3); //true
Boolean(-3); //true
updateObservers_
函數
const activeObservers = this.observers_.filter((observer) => {
// 只返回observer.hasActive()為true的observe
return observer.gatherActive(), observer.hasActive();
});
connect_
方法
transitionend
事件的事件处理函数,在某个 CSS transition
完成时触发。
DOMSubtreeModified
:在 DOM 结构中发生任何变化时触发;
// Check if MutationObserver is available.
const mutationObserverSupported = typeof MutationObserver !== "undefined";
MutationObserver 例子
MutationObserver
接口提供了监视对 DOM 树所做更改的能力。它被设计为旧的 Mutation Events
功能的替代品,该功能是 DOM3 Events
规范的一部分。
MutationObserver()
構造函數 创建并返回一个新的 MutationObserver
它会在指定的 DOM 发生变化时被调用。其中有三個方法:
disconnect()
,阻止 MutationObserver 实例继续接收的通知,直到再次调用其 observe() 方法,该观察者对象包含的回调函数都不会再被调用。observe()
,配置 MutationObserver 在 DOM 更改匹配给定选项时,通过其回调函数开始接收通知。takeRecords()
,从 MutationObserver 的通知队列中删除所有待处理的通知,并将它们返回到 MutationRecord 对象的新 Array 中。
// 选择需要观察变动的节点
const targetNode = document.getElementById("some-id");
// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };
// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
// Use traditional 'for loops' for IE 11
for (let mutation of mutationsList) {
if (mutation.type === "childList") {
console.log("A child node has been added or removed.");
} else if (mutation.type === "attributes") {
console.log("The " + mutation.attributeName + " attribute was modified.");
}
}
};
// 创建一个观察器实例并传入回调函数
const observer = new MutationObserver(callback);
// 以上述配置开始观察目标节点
observer.observe(targetNode, config);
// 之后,可停止观察
observer.disconnect();
onTransitionEnd_
函數
/**
* "Transitionend" event handler.
*
* @private
* @param {TransitionEvent} event
* @returns {void}
*/
onTransitionEnd_({ propertyName = "" }) {
// Detect whether transition may affect dimensions of an element.
const isReflowProperty = transitionKeys.some((key) => {
return !!~propertyName.indexOf(key);
});
if (isReflowProperty) {
this.refresh();
}
}
生成了一份PDF文档,需要的可以点击链接下载