列表页曝光埋点实现
以商品为例
要求
- 商品一半以上出现在视窗中时 上报该行的商品
- 快速滑动过去的商品不上报
- 滑动过程中如果一行商品一直未消失在视野中(一半以上),不能重复上报
- 滑出视野的商品,再次滑入视野时需要再次上报
分析
需要以下信息
- 商品所在行的高度rowHeight(固定值)
- 商品的可视区域的高度contentHeight(半固定值,不考虑浏览器的resize)
- 可视区域距离视窗顶部的高度headHeight(固定值)
- content的滚动高度(与scroll事件相关,考虑到滑动快时不触发上报,需要throttle)
实现
/**
* 滚动事件处理
* @param {number} headHeight content区域距离顶部的高度
* @param {number} rowHeight 每一行的高度
* @returns {Function}
*/
export function handleScroll(headHeight, rowHeight) {
let lastActive = []
let deactived = []
/**
* @param {number} contentTop 区域的top值
* @return {Array} 当前活跃的的行
*/
return function(contentTop) {
let topDiff = contentTop - headHeight
// 可视区域高度
let visibleHeight =
window.innerHeight - (topDiff <= 0 ? headHeight : contentTop)
/**
* 当前能显示的行数
* 显露一半就需要上报 则使用四舍五入
*/
let rowCount = Math.round(visibleHeight / rowHeight)
/**
* 获取当前显示的下标
*/
let index = topDiff > 0 ? 0 : Math.round(-topDiff / rowHeight)
let _active = Array.from({ length: rowCount }).reduce(
(pre, cur, i) => pre.concat(index + i),
[]
)
/**
* 之前上报过,未从屏幕上消失过的 不上报
* 之前上报过,从屏幕中消失又出现的 上报
*/
let active = _active.filter(
v => !lastActive.includes(v) || deactived.includes(v)
)
/**
* 收集非活跃状态的行,只收集滚上去的元素,active下面的行属于待活跃状态,由于和行的总数相关(商品的总行数知道与否不影响上报),会额外增加不必要的工作 所以此处不做考虑
*/
deactived = Array.from({ length: index }).map((val, i) => i)
/**
* 上次活跃的行,用来避免重复上报
*/
lastActive = [].concat(deactived).concat(_active)
return {
lastActive,
active,
deactived
}
}
}
图示
使用
let target = document.getElementById('wrapper')
let onScroll = handleScroll(100, 420)
let _scroll = _.throttle(function(){
let row = onScroll(target.getBoundingClientRect().y)
// 此时row.active就是需要上报的行的下标,active可能为空数组
...
}, 1000)
target.addEventListener('scroll', _scroll)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。