曝光元素埋点方法

Sunday, April 2, 2023

埋点方法

曝光埋点在各类埋点中是最复杂的。虽然点击埋点可能只是代码实现上增加了繁琐,但曝光埋点涉及的内容更加广泛,且如果实现不当,准确性可能会受到显著影响。

在滚动视图中,曝光检测通常依赖于监听滚动事件,结合 DOM 接口获取元素的实时坐标,计算其是否与可视区域重叠以判断是否“可见”。这种方法的准确性较高,且兼容性良好,适用于大多数场景。然而,它的缺点在于计算量大,性能损耗显著。实时监听滚动事件并进行计算,即使采用防抖或节流技术,仍会消耗较多资源。在首屏加载或复杂页面中,用户的滑动操作可能会导致频繁触发计算,从而引发性能问题,造成页面卡顿。

(此外,复杂性较高,例如数据刷新后需要重新计算,即使同一元素位置不同内容的展示,曝光也需分别计算。 — 怎么解决呢? )

因此,使用 IntersectionObserver 进行曝光处理是一个更优的选择。IntersectionObserver 是 Chrome 在 2016 年推出的新特性,目前已被主流浏览器广泛支持。它可以异步检测元素的可见性,无需频繁调用事件监听。当观察的元素交叉状态发生变化时,会自动触发回调函数。这种方法不仅提高了性能,还能有效应用于懒加载和无限滚动等场景。

小程序端

let timerMap = new Map()

Page({
  onLoad() {
    // component一般需要传当前页面或组件实例
    this._observer = wx.createIntersectionObserver(this, {
      thresholds: [0.5],
      // observeAll: true // 如果有多个item,可一次性绑定相同class
    })
    this._observer = this._observer.relativeTo('.scroll-view')
    this._observer.relativeToViewport({
      // bottom: -400 设置边距
    }).observe('#green', res => {
      console.log('observe', res)
      if (res.intersectionRatio > 0.5) {
        timerMap.set('#green', setTimeout(() => {
          console.log('有效曝光!', res)
          // report(res.dataset) 上报埋点
        }, 1500))
      } else { // 1.5s内触发离开回调函数,则取消上报
        clearTimeout(timerMap.get('#green'));
        timerMap.delete('#green');
        console.log('离开可视区', res)
      }
    })
  },
  onUnload() {
    if (this._observer) this._observer.disconnect()
    for (let [key, timerId] of timerMap) {
      clearTimeout(timerId);
    }
    timerMap.clear();
  }
})

web端

// 创建 IntersectionObserver 实例
const observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            console.log('元素进入视口:', entry.target);
            // 可以在这里执行懒加载或其他操作
        } else {
            console.log('元素离开视口:', entry.target);
        }
    });
}, {
    root: null, // 视口为浏览器窗口
    rootMargin: '0px', // 根元素的边距
    threshold: 0.1 // 交叉比例阈值
});

// 观察目标元素
const targetElement = document.querySelector('.target');
observer.observe(targetElement);

// web端由于需要先获取dom实例进行绑定,因此,我们可以利用 WeakMap 的弱引用特性,将 DOM 实例作为键存储相关的定时器,这样在页面销毁时,与之绑定的定时器会自动被清理,无需手动管理。

const timerMap = new WeakMap();

// 设置定时器并存储引用
timerMap.set(element, setTimeout(() => {
  // 处理逻辑
}, 1000));

// 当元素被销毁时,WeakMap 中的项会被自动清除