增加商品客服显示
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
// utils/ObserverInteraction.js
|
||||
|
||||
class ObserverInteraction {
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
attributes: true, // 观察属性变化
|
||||
childList: false, // 观察子节点变化
|
||||
subtree: false, // 观察后代节点
|
||||
attributeFilter: [], // 观察特定属性
|
||||
attributeOldValue: false, // 记录属性旧值
|
||||
...options,
|
||||
};
|
||||
|
||||
this.observer = null;
|
||||
this.callbacks = new Map();
|
||||
this.targets = new Set();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始观察目标元素
|
||||
* @param {HTMLElement} target - 要观察的DOM元素
|
||||
* @param {Function} callback - 变化时的回调函数
|
||||
* @param {Object} customOptions - 自定义观察选项
|
||||
*/
|
||||
observe(target, callback, customOptions = {}) {
|
||||
if (!target) {
|
||||
console.error("ObserverInteraction: 目标元素不存在");
|
||||
return;
|
||||
}
|
||||
|
||||
const options = { ...this.options, ...customOptions };
|
||||
|
||||
// 如果已存在观察器,先断开
|
||||
if (this.observer) {
|
||||
this.disconnect();
|
||||
}
|
||||
|
||||
// 创建新的观察器
|
||||
this.observer = new MutationObserver((mutationsList) => {
|
||||
const changes = this._processMutations(mutationsList);
|
||||
if (changes.length > 0 && callback) {
|
||||
callback(changes, target);
|
||||
}
|
||||
|
||||
// 触发所有注册的回调
|
||||
this.callbacks.forEach((cb, element) => {
|
||||
if (element === target || element.contains(target)) {
|
||||
cb(changes, target);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 开始观察
|
||||
this.observer.observe(target, options);
|
||||
this.targets.add(target);
|
||||
|
||||
// 存储回调
|
||||
if (callback) {
|
||||
this.callbacks.set(target, callback);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听特定属性的变化
|
||||
* @param {HTMLElement} target - 目标元素
|
||||
* @param {string} attributeName - 属性名
|
||||
* @param {Function} callback - 回调函数
|
||||
*/
|
||||
watchAttribute(target, attributeName, callback) {
|
||||
return this.observe(target, callback, {
|
||||
attributes: true,
|
||||
attributeFilter: [attributeName],
|
||||
attributeOldValue: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听元素大小变化(使用 ResizeObserver)
|
||||
* @param {HTMLElement} target - 目标元素
|
||||
* @param {Function} callback - 回调函数
|
||||
*/
|
||||
observeResize(target, callback) {
|
||||
if (!window.ResizeObserver) {
|
||||
console.warn("ResizeObserver 不支持当前浏览器");
|
||||
return;
|
||||
}
|
||||
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const { width, height } = entry.contentRect;
|
||||
callback({ width, height, target: entry.target });
|
||||
}
|
||||
});
|
||||
|
||||
resizeObserver.observe(target);
|
||||
|
||||
// 存储以便清理
|
||||
this.resizeObservers = this.resizeObservers || new Map();
|
||||
this.resizeObservers.set(target, resizeObserver);
|
||||
|
||||
return () => this.unobserveResize(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 mutations 数据
|
||||
* @private
|
||||
*/
|
||||
_processMutations(mutationsList) {
|
||||
const changes = [];
|
||||
|
||||
for (const mutation of mutationsList) {
|
||||
const change = {
|
||||
type: mutation.type,
|
||||
target: mutation.target,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
if (mutation.type === "attributes") {
|
||||
change.attributeName = mutation.attributeName;
|
||||
change.oldValue = mutation.oldValue;
|
||||
change.newValue = mutation.target.getAttribute(mutation.attributeName);
|
||||
}
|
||||
|
||||
if (mutation.type === "childList") {
|
||||
change.addedNodes = Array.from(mutation.addedNodes);
|
||||
change.removedNodes = Array.from(mutation.removedNodes);
|
||||
}
|
||||
|
||||
changes.push(change);
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册交互事件
|
||||
* @param {HTMLElement} element - 元素
|
||||
* @param {string} eventType - 事件类型
|
||||
* @param {Function} callback - 回调
|
||||
*/
|
||||
on(element, eventType, callback) {
|
||||
if (!element || !callback) return;
|
||||
|
||||
const handler = (event) => {
|
||||
callback(event, element);
|
||||
};
|
||||
|
||||
element.addEventListener(eventType, handler);
|
||||
|
||||
// 存储以便清理
|
||||
this.eventHandlers = this.eventHandlers || new Map();
|
||||
if (!this.eventHandlers.has(element)) {
|
||||
this.eventHandlers.set(element, new Map());
|
||||
}
|
||||
this.eventHandlers.get(element).set(eventType, handler);
|
||||
|
||||
return () => this.off(element, eventType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听
|
||||
* @param {HTMLElement} element - 元素
|
||||
* @param {string} eventType - 事件类型
|
||||
*/
|
||||
off(element, eventType) {
|
||||
if (!this.eventHandlers?.has(element)) return;
|
||||
|
||||
const handlers = this.eventHandlers.get(element);
|
||||
if (eventType) {
|
||||
const handler = handlers.get(eventType);
|
||||
if (handler) {
|
||||
element.removeEventListener(eventType, handler);
|
||||
handlers.delete(eventType);
|
||||
}
|
||||
} else {
|
||||
handlers.forEach((handler, type) => {
|
||||
element.removeEventListener(type, handler);
|
||||
});
|
||||
this.eventHandlers.delete(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 观察元素可见性(Intersection Observer)
|
||||
* @param {HTMLElement} target - 目标元素
|
||||
* @param {Function} callback - 可见性变化回调
|
||||
* @param {Object} options - IntersectionObserver 配置
|
||||
*/
|
||||
observeVisibility(target, callback, options = {}) {
|
||||
const defaultOptions = {
|
||||
threshold: 0.1,
|
||||
rootMargin: "0px",
|
||||
};
|
||||
|
||||
const intersectionObserver = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
callback({
|
||||
isIntersecting: entry.isIntersecting,
|
||||
intersectionRatio: entry.intersectionRatio,
|
||||
boundingClientRect: entry.boundingClientRect,
|
||||
target: entry.target,
|
||||
});
|
||||
});
|
||||
},
|
||||
{ ...defaultOptions, ...options }
|
||||
);
|
||||
|
||||
intersectionObserver.observe(target);
|
||||
|
||||
this.intersectionObservers = this.intersectionObservers || new Map();
|
||||
this.intersectionObservers.set(target, intersectionObserver);
|
||||
|
||||
return () => this.unobserveVisibility(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止观察可见性
|
||||
* @param {HTMLElement} target - 目标元素
|
||||
*/
|
||||
unobserveVisibility(target) {
|
||||
const observer = this.intersectionObservers?.get(target);
|
||||
if (observer) {
|
||||
observer.unobserve(target);
|
||||
this.intersectionObservers.delete(target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止观察大小变化
|
||||
* @param {HTMLElement} target - 目标元素
|
||||
*/
|
||||
unobserveResize(target) {
|
||||
const observer = this.resizeObservers?.get(target);
|
||||
if (observer) {
|
||||
observer.unobserve(target);
|
||||
this.resizeObservers.delete(target);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开所有观察器
|
||||
*/
|
||||
disconnect() {
|
||||
if (this.observer) {
|
||||
this.observer.disconnect();
|
||||
this.observer = null;
|
||||
}
|
||||
|
||||
// 清理 ResizeObservers
|
||||
this.resizeObservers?.forEach((observer) => {
|
||||
observer.disconnect();
|
||||
});
|
||||
this.resizeObservers?.clear();
|
||||
|
||||
// 清理 IntersectionObservers
|
||||
this.intersectionObservers?.forEach((observer) => {
|
||||
observer.disconnect();
|
||||
});
|
||||
this.intersectionObservers?.clear();
|
||||
|
||||
this.targets.clear();
|
||||
this.callbacks.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁实例
|
||||
*/
|
||||
destroy() {
|
||||
this.disconnect();
|
||||
|
||||
// 清理所有事件监听
|
||||
this.eventHandlers?.forEach((handlers, element) => {
|
||||
handlers.forEach((handler, type) => {
|
||||
element.removeEventListener(type, handler);
|
||||
});
|
||||
});
|
||||
this.eventHandlers?.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export default ObserverInteraction;
|
||||
Reference in New Issue
Block a user