Posted in

【第 3320 期】如何实现元素的曝光监测_AI阅读总结 — 包阅AI

包阅导读总结

1.

关键词:元素曝光监测、IntersectionObserver、MutationObserver、可见比例、有效停留时长

2.

总结:本文介绍了页面元素曝光监测的实现方法,包括相关名词解释、需考虑的问题及总体实现,重点提及可见比例和有效停留时长的设定,以及利用IntersectionObserver和MutationObserver进行监听的方式。

3.

主要内容:

– 名词解释

– 曝光:页面元素被用户浏览。

– 视图元素:页面上展示的元素、组件或模块。

– 可见比例:视图元素在可视区域面积与整体面积之比。

– 有效停留时长:元素满足可见比例并保持可见的持续时间。

– 重复曝光:同一页面元素在特定条件下的多次曝光。

– 监测曝光需考虑的问题

– 曝光条件:可见比例和有效停留时长达到设定值。

– 检测可见比例:使用IntersectionObserver api及threshold配置项。

– 监听动态元素:先监听DOM元素挂载或卸载,使用MutationObserver监听DOM变更。

– 监听停留时长:维护观察列表记录元素曝光开始时间,计算停留时长。

– 总体实现

– 实现exposure方法,传入检测曝光的元素信息。

– 初始化时查找已渲染元素,使用IntersectionObserver监听。

– 对动态渲染元素用MutationObserver监听。

– 维护observe列表,处理元素曝光过程,设置定时器检查列表。

– 给出代码实现及相关解释。

思维导图:

文章地址:https://mp.weixin.qq.com/s/Cz8YVCszEJk18RZc-UkZFw

文章来源:mp.weixin.qq.com

作者:霁明

发布时间:2024/7/20 0:22

语言:中文

总字数:2023字

预计阅读时间:9分钟

评分:86分

标签:前端技术,曝光监测,IntersectionObserver,MutationObserver,前端性能


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

前言

介绍了如何实现页面元素曝光监测的技术方案和实现步骤。今日前端早读课文章由 @霁明分享,由 @袋鼠云数栈 UED 团队授权。

正文从这开始~~

一些名词解释

曝光:页面上某一个元素、组件或模块被用户浏览了,则称这个元素、组件或模块被曝光了。

视图元素:将页面上展示的元素、组件或模块统称为视图元素。

可见比例:视图元素在可视区域面积 / 视图元素整体面积。

有效停留时长:视图元素由不可见到可见,满足可见比例并且保持可见状态的持续的一段时间。

重复曝光:在同一页面,某个视图元素不发生 DOM 卸载或页面切换的情况下,发生的多次曝光称为重复曝光。例如页面上某个视图元素,在页面来回滚动时,则会重复曝光。

如何监测曝光

需要考虑的一些问题

曝光条件

页面上某一视图元素的可见比例达到一定值(例如 0.5),且有效停留时间达到一定时长(例如 500ms), 则称该视图元素被曝光了。

如何检测可见比例

使用 IntersectionObserver api 对元素进行监听,通过 threshold 配置项设置可见比例,当达到可见比例时,观察器的回调就会执行。

【第2535期】浏览器的5种Observer

IntersectionObserver 使用示例:

 let callback = (entries, observer) => {
entries.forEach((entry) => {
// 每个条目描述一个目标元素观测点的交叉变化:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
let options = {
threshold: 1.0,
};
let observer = new IntersectionObserver(callback, options);

let target = document.querySelector("#listItem");
observer.observe(target);

如何监听动态元素

使用 IntersectionObserver 对元素进行监听之前,需要先获取到元素的 DOM,但对于一些动态渲染的元素,则无法进行监听。所以,需要先监听 DOM 元素是否发生挂载或卸载,然后对元素动态使用 IntersectionObserver 进行监听,可以使用 MutationObserver 对 DOM 变更进行监听。MutationObserver 的使用示例:

 // 选择需要观察变动的节点
const targetNode = document.getElementById("some-id");

// 观察器的配置(需要观察什么变动)
const config = { attributes: true, childList: true, subtree: true };

// 当观察到变动时执行的回调函数
const callback = function (mutationsList, observer) {
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();

如何监听停留时长

维护一个观察列表,元素可见比例满足要求时,将该元素信息(包含曝光开始时间)添加到列表,当元素退出可视区域时(可见比例小于设定值),用当前时间减去曝光开始时间,则可获得停留时长。

总体实现

实现一个 exposure 方法,支持传入需要检测曝光的元素信息(需包含 className),使用 IntersectionObserver 和 MutationObserver 对元素进行动态监听。

  • 初始化时,根据 className 查找出已渲染的曝光监测元素,然后使用 IntersectionObserver 统一监听,如果有元素发生曝光,则触发对应曝光事件;

  • 对于一些动态渲染的曝光监测元素,需要使用 MutationObserver 监听 dom 变化。当有节点新增时,新增节点若包含曝光监测元素,则使用 IntersectionObserver 进行监听;当有节点被移除时,移除节点若包含曝光监测元素,则取消对其的监听;

  • 维护一个 observe 列表,元素开始曝光时将元素信息添加到列表,元素退出曝光时如果曝光时长符合规则,则触发对应曝光事件,并在 observe 列表中将该元素标记为已曝光,已曝光后再重复曝光则不进行采集。如果元素在 DOM 上被卸载,则将该元素在 observe 列表中的曝光事件删除,下次重新挂载时,则重新采集。

  • 设置一个定时器,定时检查 observe 列表,若列表中有未完成曝光且符合曝光时长规则的元素,则触发其曝光事件,并更新列表中曝光信息。

初始化流程

元素发生挂载或卸载过程

元素曝光过程

代码实现
 const exposure = (trackElems?: ITrackElem[]) => {
const trackClassNames =
trackElems
?.filter((elem) => elem.eventType === TrackEventType.EXPOSURE)
.map((elem) => elem.className) || [];

const intersectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
const entryElem = entry.target;
const observeList = getObserveList();
let expId = entryElem.getAttribute(EXPOSURE_ID_ATTR);

if (expId) {
// 若已经曝光过,则不进行采集
const currentItem = observeList.find((o) => o.id === expId);
if (currentItem.hasExposed) return;
}

if (entry.isIntersecting) {
if (!expId) {
expId = getRandomStr(8);
entryElem.setAttribute(EXPOSURE_ID_ATTR, expId);
}
const exit = observeList.find((o) => o.id === expId);
if (!exit) {
// 把当前曝光事件推入observe列表
const trackElem = trackElems.find((item) =>
entryElem?.classList?.contains(item.className)
);
const observeItem = { ...trackElem, id: expId, time: Date.now() };
observeList.push(observeItem);
setObserveList(observeList);
}
} else {
if (!expId) return;
const currentItem = observeList.find((o) => o.id === expId);
if (currentItem) {
if (Date.now() - currentItem.time > 500) {
// 触发曝光事件,并更新observe列表中的曝光信息
tracker.track(
currentItem.event,
TrackEventType.EXPOSURE,
currentItem.params
);
currentItem.hasExposed = true;
setObserveList(observeList);
}
}
}
});
},
{ threshold: 0.5 }
);

const observeElems = (queryDom: Element | Document) => {
trackClassNames.forEach((name) => {
const elem = queryDom.getElementsByClassName?.(name)?.[0];
if (elem) {
intersectionObserver.observe(elem);
}
});
};

const mutationObserver = new MutationObserver((mutationList) => {
mutationList.forEach((mutation) => {
if (mutation.type !== 'childList') return;

mutation.addedNodes.forEach((node: Element) => {
observeElems(node);
});

mutation.removedNodes.forEach((node: Element) => {
trackClassNames.forEach((item) => {
const elem = node.getElementsByClassName?.(item)?.[0];
if (!elem) return;
const expId = elem.getAttribute('data-exposure-id');
if (expId) {
const observeList = getObserveList();
const index = observeList.findIndex((o) => o.id === expId);
if (index > -1) {
// 元素被卸载时,将其曝光事件从列表删除
observeList.splice(index, 1);
setObserveList(observeList);
}
}
intersectionObserver.unobserve(elem);
});
});
});
});

observeElems(document);
mutationObserver.observe(document.body, {
subtree: true,
childList: true,
});

const timer = setInterval(() => {
// 检查observe队列,若队列中有符合曝光时长规则的元素,则修改曝光状态,并触发曝光事件。
const observeList = getObserveList();
let shouldUpdate = false;
observeList.forEach((o) => {
if (!o.hasExposed && Date.now() - o.time > 500) {
tracker.track(o.event, TrackEventType.EXPOSURE, o.params);
o.hasExposed = true;
shouldUpdate = true;
}
});
if (shouldUpdate) {
setObserveList(observeList);
}
}, 3000);

return () => {
mutationObserver.disconnect();
intersectionObserver.disconnect();
clearInterval(timer);
removeObserveList();
};
};

export default exposure;

观点

  • 曝光监测是衡量用户与页面元素交互的重要指标,需要考虑可见比例和有效停留时长等因素。

  • IntersectionObserver API 是监测元素曝光的首选方法,可以通过设置 threshold 来定义可见比例。

  • 对于动态渲染的元素,需要使用 MutationObserver 来监听 DOM 变化,确保能够及时监听到新增或移除的元素。

  • 监听元素的停留时长需要维护一个观察列表,记录元素曝光的开始和结束时间,以计算停留时长。

  • 实现元素曝光监测的方法应包括对已渲染和动态渲染元素的监听、维护观察列表、处理元素的挂载和卸载、以及定时检查观察列表。

关于本文
作者:@霁明
原文:https://juejin.cn/post/7391117519779069991

这期前端早读课
对你有帮助,帮”
“一下,
期待下一期,帮”
在看” 一下 。