页面停留时间(Time on Page)简称 Tp,是网站分析中很常见的一个指标,用于反映用户在某些页面上停留时间的长短,传统的Tp统计方法会存在一定的统计盲区,比如无法监控单页应用,没有考虑用户切换Tab、最小化窗口等操作场景。基于上述背景,重新调研和实现了精确统计页面停留时长的方案,需要 兼容单页应用和多页应用,并且不耦合或入侵业务代码。
虽然百度统计之类的也可以记录用户的浏览行为,但是这类统计是全部跟踪用户,而无法精确的跟踪到注册的用户之前一系列的行为,而我们只需要针对注册用户进行有目的性的行为分析。。。
对于单页应用内部的跳转可以转化为两个问题:
首先确定一下需求:
对于常规页面的 首次加载、页面关闭、刷新 等操作都可以通过 window.onload 和window.onbeforeunload 事件来监听页面进入和离开,浏览器前进后退可以通过 pageshow 和 pagehide 处理。
首先,进入页面的时候,浏览器会调用onload事件,浏览器关闭时,浏览器会调用beforeunload事件,可以从这两个事件入手。
但是这里有几个问题,那就是浏览器刷新的时候,也会调用beforeunload,还有就是如何知道用户在其它标签页也打开了网页呢?
有什么可以记录浏览器是否刷新呢?这时候我想到了sessionStorage。
sessionStorage,用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。也就是说数据存储在sessionStorage中,在当前标签页会一直存在。
首先在初次进入页面时在onload事件中往sessionStorage存入一个标识
window.addEventListener("load",function(){
if(sessionStorage.getItem("flag")){
return;
}
sessionStorage.setItem("flag", true);
})
上面说的还有一个问题,就是如何统计用户打开了多个标签页呢?
在本地缓存中,localStorage中的数据是整个网站都可以共享的,也就是具有相同域名、ip的网站,localStorage里的数据在不同标签页中也是相同的。
这时候可以往localStorage里存一个计数器,当用户进入页面时,计数器加1,当用户离开页面时计数器减1。当计数器为0时,标志页面全部关闭,此时可以记录页面关闭的结束时间。同时访问网站的开始时间也存到localStorage中。
window.addEventListener("load",function(){
let nowTime = getNowTime()
let openPageCount = localStorage.getItem("openPageCount")
openPageCount += 1
localStorage.setItem("openPageCount",openPageCount)
//其实说明是页面是刷新后加载的,不应该统计开始时间
if(sessionStorage.getItem("flag")){
return;
}
localStorage.setItem("startTime",nowTime)
sessionStorage.setItem("flag", true);
})
window.addEventListener("beforeunload",function(){
let nowTime = getNowTime()
let openPageCount = localStorage.getItem("openPageCount")
openPageCount -= 1
localStorage.setItem("endTime",nowTime)
localStorage.setItem("openPageCount",openPageCount)
if(openPageCount == 0){
//说明页面全部关闭,这时候可以传数据给后台
}
})
function getNowTime(){
let nowTime = new Date().getTime()
return nowTime
}
这里还有个问题,就是用户意外关掉电脑或者突然断电了,这时候是不会执行beforeunload是不会执行,此时这个数据是不正确,是应该抛弃的。
要解决这个问题,可以往localStorage里存一个刷新时间,每30秒更新这个刷新时间,用户加载网站时,读取这个刷新时间,如果当前时间和这个刷新时间相差大于1分钟,说明数据有断层,此时应该抛弃这一条记录数据。
window.addEventListener("load",function(){
let nowTime = getNowTime()
let openPageCount = localStorage.getItem("openPageCount")
openPageCount = openPageCount ? parseInt(openPageCount) : 0
openPageCount += 1
localStorage.setItem("openPageCount",openPageCount)
refreshTime()
let isRightTime = compareRefreshTime()
//其实说明是页面是刷新后加载的,不需要统计开始时间
if(sessionStorage.getItem("flag") && isRightTime){
return;
}
localStorage.setItem("startTime",nowTime)
localStorage.setItem("refreshTime",nowTime)
sessionStorage.setItem("flag", true);
})
window.addEventListener("beforeunload",function(){
let nowTime = getNowTime()
let openPageCount = localStorage.getItem("openPageCount")
openPageCount -= 1
localStorage.setItem("endTime",nowTime)
localStorage.setItem("openPageCount",openPageCount)
if(openPageCount == 0){
//说明页面全部关闭,这时候可以传数据给后台
}
})
function getNowTime(){
let nowTime = new Date().getTime()
return nowTime
}
function compareRefreshTime(){
let nowTime = getNowTime()
let refreshTime = localStorage.getItem("refreshTime")
return nowTime - refreshTime < 60 * 1000
}
function refreshTime(){
setTimeout(refreshTime ,30 * 1000)
let nowTime = getNowTime()
localStorage.setItem("refreshTime",nowTime)
}
可以通过 Page Visibility API 以及在 window 上声明 onblur/onfocus 事件来处理。
Page Visibility API
一个网页的可见状态可以通过 Page Visibility API 获取,比如当用户 切换浏览器Tab、最小化窗口、电脑睡眠 的时候,系统API会派发一个当前页面可见状态变化的 visibilitychange 事件,然后在事件绑定函数中通过 document.hidden 或者 document.visibilityState 读取当前状态。
document.addEventListener('visibilitychange', function (event) {
console.log(document.hidden, document.visibilityState)
})
onblur/onfocus
可以通过 Page Visibility API 以及在 window 上声明 onblur/onfocus 事件来处理。对于PC端来说,除了监听上述相关事件外,还可以考虑监听鼠标行为,比如当一定时间内鼠标没有操作则认为用户处于非活跃状态。
页面离开时上报
对于页面刷新或者关闭窗口触发的操作可能会造成数据丢失
下次打开页面时上报
会丢失历史访问记录中的最后一个页面数据
目前采用的方案2,对于单页内部跳转是即时上报,对于单页/多页应用触发 window.onbeforeunload 事件的时候会把当前页面数据暂存在 localStorage 中,当用户下次进入页面的时候会把暂存数据上报。有个细节问题,如果用户下次打开页面是在第二天,对于统计当天的活跃时长会有一定的误差,所以在数据上报的同时会把该条数据的页面进入时间/离开时间带上。
以上就是统计用户停留网站时长思路的全部代码。
对于页面停留时长的定义可能在不同场景会有差异,比如内部业务系统或者OA系统,产品可能更关心用户在页面的活跃时长;而对于资讯类型的产品,页面可见时长会更有价值。单一的数据对业务分析是有限的,所以在具体的代码实过程中我们会把停留时长分三个指标,这样能更好的帮助产品/运营分析。