在 Web 开发中,我们经常需要埋点统计用户的行为,比如 元素曝光 统计,即某个元素在视口中可见并达到一定时间后触发上报。为了解决这一需求,我们可以使用 IntersectionObserver
监听元素的可见性,并结合 setTimeout
计算停留时间,确保符合条件后才触发上报。
需求分析
我们的曝光埋点 SDK 需要满足以下需求:
- 监听多个元素的可见性
- 设定曝光阈值(元素可见面积的比例,比如 50% 才算曝光)
- 设定曝光时间(元素需要在视口内持续一段时间才触发上报)
- 动态解绑监听(防止内存泄漏)
代码实现
1class ExposureTracker {
2 constructor({ threshold = 0.5, duration = 1000, callback } = {}) {
3 this.threshold = threshold;
4 this.duration = duration;
5 this.callback = callback;
6 this.observedElements = new Map();
7
8 this.observer = new IntersectionObserver(this.handleIntersect.bind(this), {
9 threshold: Array.from({ length: 10 }, (_, i) => (i + 1) / 10),
10 });
11 }
12
13 observe(element, data = {}) {
14 if (!element) return;
15 this.observedElements.set(element, { isVisible: false, timer: null, data });
16 this.observer.observe(element);
17 }
18
19 unobserve(element) {
20 if (this.observedElements.has(element)) {
21 clearTimeout(this.observedElements.get(element).timer);
22 this.observer.unobserve(element);
23 this.observedElements.delete(element);
24 }
25 }
26
27 handleIntersect(entries) {
28 entries.forEach((entry) => {
29 const { target, intersectionRatio } = entry;
30 const record = this.observedElements.get(target);
31 if (!record) return;
32
33 const isVisible = intersectionRatio >= this.threshold;
34 if (isVisible && !record.isVisible) {
35
36 record.timer = setTimeout(() => {
37 this.callback && this.callback(record.data);
38 this.unobserve(target);
39 }, this.duration);
40 } else if (!isVisible && record.isVisible) {
41
42 clearTimeout(record.timer);
43 }
44
45 record.isVisible = isVisible;
46 });
47 }
48}
49
50
51const tracker = new ExposureTracker({
52 threshold: 0.5,
53 duration: 2000,
54 callback: (data) => console.log('曝光上报:', data),
55});
56
57document.querySelectorAll('.track').forEach((el, index) => {
58 tracker.observe(el, { id: index, message: `元素${index}曝光` });
59});
代码解析
-
使用
IntersectionObserver
监听元素的可见性threshold
设定多个阈值,确保可以检测到不同的可见比例。- 当
intersectionRatio
大于等于设定的threshold
时,认为元素曝光。
-
使用
setTimeout
处理曝光时间- 只有当元素的可见比例满足
threshold
,且持续 超过duration
毫秒 后,才会触发回调。 - 如果元素在
duration
时间内消失,则取消计时,避免误报。
- 只有当元素的可见比例满足
-
动态解绑,防止内存泄漏
- 在元素曝光上报后,使用
unobserve
解除监听,避免对已曝光元素重复监听。
- 在元素曝光上报后,使用
适用场景
- 统计广告、Banner 是否被用户看到
- 统计文章或图片是否真正出现在用户视野中
- 结合 A/B 测试分析不同 UI 组件的可见度和转化率
总结
通过 IntersectionObserver
,我们可以高效地监听元素的可见性变化,并结合 setTimeout
控制曝光时间,构建一个轻量级的 曝光埋点 SDK。这样,我们既能避免传统 scroll
监听带来的性能问题,又能精确统计元素的曝光情况。
个人笔记记录 2021 ~ 2025