共计 4946 个字符,预计需要花费 13 分钟才能阅读完成。
计算一个页面内每个模块的曝光时间(停留时间)
产品希望看到投放出去的活动页,用户对其页面内的什么信息比较感兴趣,对什么信息完全不感兴趣。=> 计算页面内每模块的停留时间
第一次听到这个需求,我的大脑开始疯狂运转,然后想到了 plan 1, plan 2, plan3… 中间还有很多失败想法我已经忘记了,这里方案三是我最终采用的方法。
如果大家有什么更好的想法,欢迎赐教,真心的!!!
方案一:根据页面 dom 将页面分模块
var bodyChildrenLists = $('body').children()
var bodyChildDomLsit = []
var initHeight = 0
for (var i = 0; i < bodyChildrenLists.length; i++) {if (bodyChildrenLists[i].tagName !== 'SCRIPT') {
bodyChildDomLsit.push({className: bodyChildrenLists[i].className,
height: bodyChildrenLists[i].offsetHeight
})
}
}
存在的问题:
不同人的代码风格差异性大,该方案不适合这类代码风格
<body>
<div class="container">
<div class="header"></div>
<div class="nav"></div>
<div class="footer"></div>
</div>
</body>
这种方式很好,就是,,,如果大家的代码风格很一致的情况下使用比较好。
方案二:计算出用户打开页面后的所有行为
var scrollTop = 0
var time = Date.now()
window._stayStatus = {
// 记录运动轨迹, down > 1 向下移动 down 向上移动, sliderDis 移动距离, time 移动耗时, initDis 初始距离, initTime 初始时间
moveData: [],
enterTime: Date.now()}
var moveData = window._stayStatus.moveData
var currentMoveIndex = 0
function move () {var currentTime = Date.now()
var currentScrollTop = $(window).scrollTop()
var dis = currentScrollTop - scrollTop
var disTime = currentTime - time
// 上一次滑动页面和这次滑动页面的时间差大于 100ms,就视作用户在某一个段时间做了停留
if (disTime > 100) {if (moveData[currentMoveIndex] && moveData[currentMoveIndex].down === 0) {moveData[currentMoveIndex].time += disTime
} else {
moveData.push({
down: 0,
initTime: time, // initTime 表示进入该状态的初始时间
initDis: currentScrollTop, //initDis 表示进入该状态的初始位置
sliderDis: dis, // 在该状态内滑动的距离
time: disTime // 在该状态经历的时间(ms)})
}
} else {
// 向下滑动
if (dis >= 0) {
// 如果之前已经是向下滑动的状态,只需要在原来的数据上累加滑动距离和滑动时间
if (moveData[currentMoveIndex] && moveData[currentMoveIndex].down > 0) {moveData[currentMoveIndex].sliderDis += dis
moveData[currentMoveIndex].time += disTime
} else {
moveData.push({
down: 1,
initTime: currentTime,
initDis: currentScrollTop,
sliderDis: dis,
time: disTime
})
}
} else {if (moveData[currentMoveIndex] && moveData[currentMoveIndex].down < 0) {moveData[currentMoveIndex].sliderDis += dis
moveData[currentMoveIndex].time += disTime
} else {
moveData.push({
down: -1,
initTime: currentTime,
initDis: currentScrollTop,
sliderDis: dis,
time: disTime
})
}
}
}
currentMoveIndex = moveData.length - 1
time = currentTime
scrollTop = currentScrollTop
}
window.onscroll = function (e) {move()
}
根据以上方法获取到的数据如下:
表示:用户在距顶部 2px 时停留了 2728ms 后,向下滑动了 612px,滑动时间为 595ms,然后又在距顶部 612px 停留了 8649ms, 最后向上滑动了 604px, 经历了 167ms。
存在的问题:
最后得到的数据量虽然不会很大,但是将这样的数据给数据组分析,存在一定的难度。这是在没有和产品对接时,自己想的办法,有点想复杂了。但是这种方式可以比较生动模拟出用户的行为。
方案三:固定模块尺寸,计算每模块的停留时间
与我的产品对接了基本的规定:
- 每 1300px 高度作为为一个模块,进行埋点统计。
- 每屏曝光范围大于 400px 时作为有效曝光,开始记录时长。
- 每一模块的上报时间均为在模块内滑动时间及静止停留时间加和。
- 设当前模块为模块 0,当用户未到达模块 1 时,反复滑动时间均记做模块 0 内时间。
- 设当前模块为模块 0,当用户到达模块 1 后又通过滑动行为返回模块 0,此时会重新记录一次模块 0 数据
- 最后一次上报时间为用户离开该页面(进入下一流程页面或关闭浏览器),需统计能够监测到的用户离开行为及场景。
根据以上需求,我做了一个小 demo,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="//lib04.xesimg.com/lib/jQuery/1.11.1/jquery.min.js"> </script>
<script src="//zt.xueersi.com/apStatic/js/qz-rem.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.section {
height: 1300px;
border-bottom: 1px solid #f00;
box-sizing: border-box;
padding-top: 400px;
}
</style>
</head>
<body>
<div id="app">
<div class="section">
<p> 停留时间 0:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 0">{{0}} ---- {{item.time}}</p>
</div>
<div class="section">
<p> 停留时间 1:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 1">{{1}} ---- {{item.time}}</p>
</div>
<div class="section">
<p> 停留时间 2:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 2">{{2}} ---- {{item.time}}</p>
</div>
<div class="section">
<p> 停留时间 3:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 3">{{3}} ---- {{item.time}}</p>
</div>
<div class="section">
<p> 停留时间 4:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 4">{{4}} ---- {{item.time}}</p>
</div>
<div class="section">
<p> 停留时间 5:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 5">{{5}} ---- {{item.time}}</p>
</div>
<div class="section">
<p> 停留时间 6:</p>
<p v-for="item in movedata" v-if="parseInt(item.pos / 1300) === 6">{{6}} ---- {{item.time}}</p>
</div>
</div>
<script src="https://cdn.bootcss.com/vue/2.5.16/vue.min.js"></script>
<script>
new Vue({
el: '#app',
data: {movedata: [],
scrollTop: $(window).scrollTop(),
time: Date.now(),
stayTime: 0
},
mounted () {
// 部分页面存在页面滚动到某一高度时,刷新后页面也会固定在该高度的问题,初始化 movedata 数据
var index = parseInt(this.scrollTop / 1300) + 1
for (var i = 0; i <= index; i++) {
this.movedata.push({
pos: i * 1300,
time: 0
})
}
window.onscroll = () => {this.scrollTop = $(window).scrollTop()}
setInterval(() => {var currentTime = Date.now()
var disTime = currentTime - this.time
// 计算当前是展现在屏幕中的模块序号,一个屏幕内最多展现两个模块,currentIndex 永远指定的是上面的模块
var currentIndex = parseInt(this.scrollTop / 1300)
var length = this.movedata.length
if (currentIndex + 1 >= length) {for (var i = length; i <= currentIndex + 1; i++) {
this.movedata.push({
pos: 1300 * i,
time: disTime
})
}
} else {
// 计算当前的滚动高度超出满屏的多少
var modeDis = this.scrollTop - this.movedata[currentIndex].pos
// 表示一屏中上面的模块展示区域超过 400,可以累加停留时间
if ((1300 - modeDis) > 400) {this.movedata[currentIndex].time += disTime
}
// 表示一屏中下面的模块展示区域超过 400,可以累加停留时间
if (modeDis > 400) {this.movedata[currentIndex + 1].time += disTime
}
}
this.time = currentTime
}, 1000)
}
})
</script>
</body>
</html>
使用这种方式,movedata 的数组长度等于页面内的模块个数
正文完
发表至: javascript
2019-05-28