作者:w_西城
我们在处理模块曝光埋点时,需要根据页面滚动的位置判断模块是否可见(被曝光)。Web 上传统方法是增加页面 scroll 监听事件,根据滚动位置与模块位置进行对比判断,小程序上也可以使用这种方法,但现在有更便捷优雅的替代方案 —— IntersectionObserver 对象。
IntersectionObserver 对象,用于推断某些节点是否可以被用户看见,下面将介绍相关的 API:
通过 this.createIntersectionObserver
创建,该方法可传入的参数有三个:
[0,1]
),当相交到达该阈值时会触发一次监听回调,在曝光埋点场景下设置为中间位置 [0.5]
即可;设置参考区域的方法有两个:io.relativeToViewport()
和 io.relativeTo('selector', { ...margins })
,如果判断相交参考区域是窗口,则使用前者,曝光埋点的场景下就使用这个;后者可用选择器设置其他节点作为相交的参考区域。
开始监听方法:io.observe(selector, callback)
,selector代表目标模块的选择器,当它和参考区域相交达到阈值比例时,会触发 callback 回调函数,回调函数接受如下几个参数(在该场景中暂时都不会用到):
当页面退出时记得要取消监听:io.disconnect()。
我们可以设计一个类,用来处理监听相交区域的逻辑。
首先来看构造函数,代码如下:
class IntersectionObserver {
constructor(options) {
this.$options = {
context: null,
threshold: 0.5,
initialRatio: 0,
observeAll: false,
selector: null,
relativeTo: null,
onEach: res => res.dataset,
onFinal: () => null,
delay: 200,
...options,
}
this.$observer = null
}
}复制代码
显然,构造函数传入了一些重要的参数,包括 createIntersectionObserver
所需要的三个参数:thresholds, initialRatio, observeAll
和上下文 context
;设置参考区域所需的 relativeTo
;监听方法所需的目标模块选择器 selector
。
最后还有 IntersectionObserver 类监听调用时需要的三个参数:
onEach
方法;delay
后,会调用一次 onFinal
方法。在模块曝光埋点场景下,如果页面在快速滚动时,每次的监听触发都上报埋点,一时间请求会堆积很多,所以需要 onFinal
方法,在一段时间后统一上报曝光埋点;onFinal
方法的间隔时间;要想开始监听相交区域,需要先创建监听器,设置完相交区域后再开始监听,关键代码如下:
_createObserver() {
const opt = this.$options
const observerOptions = {
thresholds: [opt.threshold],
observeAll: opt.observeAll,
initialRatio: opt.initialRatio,
}
// 创建监听器
const ob = opt.context
? opt.context.createIntersectionObserver(observerOptions)
: wx.createIntersectionObserver(null, observerOptions)
// 相交区域设置
if (opt.relativeTo) ob.relativeTo(opt.relativeTo)
else ob.relativeToViewport()
// 开始监听
let finalData = []
let isCollecting = false
ob.observe(opt.selector, res => {
const { intersectionRatio } = res
const visible = intersectionRatio >= opt.threshold
if (!visible) return
const data = opt.onEach(res)
finalData.push(data)
if (isCollecting) return // 正在收集监听结果,不会调用 onFinal
isCollecting = true
// 设置延迟调用 onFinal
setTimeout(() => {
opt.onFinal.call(null, finalData)
finalData = []
isCollecting = false
}, opt.delay)
})
return ob
}
复制代码
封装对外的 _createObserver
方法:
connect() {
if (this.$observer) return this
this.$observer = this._createObserver()
return this
}
复制代码
封装停止监听的方法:
disconnect() {
if (!this.$observer) return
const ob = this.$observer
if (ob.$timer) clearTimeout(ob.$timer)
ob.disconnect()
this.$observer = null
}
复制代码
import IntersectionObserver from './intersection-observer.js';
const ob = new IntersectionObserver({...})
ob.connect()复制代码
详见代码片段:developers.weixin.qq.com/s/lqUakfmM7…
当然,曝光埋点也可以使用传统 Web 的监听 scroll 事件的方式。不过,既然小程序提供了 IntersectionObserver API 并且几乎所有客户端都已支持,那自然就用这种更方便的方式。
另外,在百度小程序和支付宝小程序上也有支持相关的API,跨端开发也不用考虑其他小程序不支持。
支付包小程序兼容性有待考证,百度可以使用 IntersectionObserver
,不过需要注意 this.createIntersectionObserver
非组件是没有的,只能使用 swan.createIntersectionObserver(this)
;第二点,createIntersectionObserver
的参数observeAll
需要改成 selectAll
(百度小程序代码片段:swanide://fragment/142c0f60156b1e850dc239553ecffe7b1571810456384)。