需求背景
一个页面A
需要记录在页面点击某个链接B
时的时间time1
,与后退返回该页面时的时间time2
,得到中间的时间差time2-time1
。
此处的难点在于:
1、点击跳出A的B是不确定的,有可能点击的是广告(点击的是广告,则几乎很难监听到点击广告的事件),有可能是一个页面链接(页面链接有可能非常多,不可能每个链接都加一个监听事件);
2、记录离开页面前的时间,与返回页面的时间,离开页面有可能是在当前页面刷新打开页面,有可能是新开窗口打开,情况有多种。
解决思路
巧用pagehide
,pageshow
,document.hidden
,visibilitychange
组合实现。
pageshow
和 pagehide
事件
手机上的浏览器有一个特性,名叫“往返缓存”(back-forward cache,或bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。如果页面位于bfcache中,那么再次打开该页面就不会触发load事件。尽管由于内存中保存了整个页面的状态,不触发load事件也不应该会导致什么问题,但为了更形象地说明bfcache的行为,Firefox还是提供了一些新事件。 第一个事件就是pageshow
,这个事件在页面显示时触发,无论页面是否来自bfcache。在重新加载页面中,pageshow会在load事件触发后触发;而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发。
1)load 和 unload 事件监听web页面的进入和离开,一般用于页面的首次加载、刷新和关闭等操作的监听;
2)pageshow
和 pagehide
事件多用于监听浏览器的前进和后退等。
1、pageshow和load区别:
pageshow 事件类似于 load 事件,load 事件在页面第一次加载时触发, pageshow 事件在每次加载页面时触发,即 load 事件在页面从浏览器缓存中读取时不触发。
一般情况下,移动端浏览器会将当前已访问页面存入缓存中,缓存中保存着页面数据,DOM和js的状态,前进和后退操作时直接从浏览器缓存中读取页面内容,而不进行页面刷新,所以监听前进和后退操作时可用pageshow事件。
触发时间:load先触发,pageshow后触发。
2、查看是否读取缓存:
为了查看页面是直接从服务器上载入还是从缓存中读取,你可以使用 Event 对象的 persisted 属性来判断。 如果页面从浏览器的缓存中读取该属性返回 ture,否则返回 false
3、示例:
window.addEventListener('pageshow', function(event) {
console.log(event.persisted);
})
4、pagehide和unload事件的区别:
pagehide 事件类似于 unload 事件,在用户离开网页时触发(如点击一个链接、刷新页面、提交表单、关闭浏览器、前进、后退等)。
页面缓存:pagehide触发可以缓存页面,但unload 事件触发后无法缓存。
触发时间:pagehide先触发,unload后触发。
2、查看是否读取缓存:
同pageshow
pageshow,pagehide兼容情况:基本移动端上用都兼容
image.png
问题:但是有时候在一些机型中event. persisted判断并不准确,所以可以引用window.performance.navigation.type
做兼容处理。
window.performance
对象
performance.navigation.type
是一个无符号短整型,接口呈现了如何导航到当前文档的信息。它有四种type类型:
1、TYPE_NAVIGATE (0)
:当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0。
2、TYPE_RELOAD (1)
:点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1:。
3、TYPE_BACK_FORWARD (2)
:页面通过历史记录和前进后退访问时。type值为2。
4、TYPE_RESERVED (255)
: 任何其他方式,type值为255。
所以type为2可以作为页面后退或者前进时的一个判断依据。
window.addEventListener('pageshow', (e) => {
if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
// 页面后退或者前进时操作
}
document.hidden
属性
页面可见性判断:document.hidden
与visibilitychange
事件,当前页面不在视野范围内,则会触发visibilitychange事件,并且改变document.hidden的值为true,当回到页面视野当中,也会触发事件改变document.hidden的值为false。
// 兼容浏览器
const hidden = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ? 'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;
const event = hidden.replace(/hidden/i, 'visibilitychange');
document.addEventListener(event, () => {
console.log('当前页面是否被隐藏:', document[hidden]);
});
具体实现
页面A点击页面中的链接B跳去新链接,记录离开A页面的时间与返回A页面的时间。
B页面离开方式:
1、当前窗口打开页面(非跳出型):监听pagehide & document.hidden=true
2、新开窗口打开页面(跳出型):监听pagehide & document.hidden=true
返回页面A方式:
1、返回后页面被动刷新:监听pageshow
2、返回后页面不刷新:监听document.hidden=false
终上所述,为了兼容设备前进后退的多种差异情况,需要融合几种监听方式,以达到覆盖多种情况的效果(经测试,在安卓版本5,7,10上组合方式都通过测试,ios也通过了测试)。
// 兼容浏览器
const hidden = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ?
'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;
const event = hidden.replace(/hidden/i, 'visibilitychange');
document.addEventListener(event, () => {
if(document[hidden]) {
// 隐藏:记录离开时间
} else {
// 显示:记录返回时间
}
});
window.addEventListener('pageshow', (e) => {
if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
// 页面后退时操作:记录返回时间
}
}
window.addEventListener('pagehide', (e) => {
// 页面离开时操作:记录离开时间
}
后记
在vue中,如果已经创建了Vue示例再实行监听pageshow时间的话,是会失效的,所以,需要在未创建Vue示例前监听pageshow事件,并且可以通过window.postMessage
延迟传递信息:
window.addEventListener('pageshow', (t) => {
// persisted:查看页面是直接从服务器上载入还是从浏览器缓存中读取,true:缓存读取
if (t.persisted || (window.performance && window.performance.navigation.type === 2)) {
setTimeout(() => {
window.postMessage('pageshowEvent', window.location.origin);
}, 2000);
}
new Vue({
el: '#app',
template: '<Main />',
components: { Main },
});
});
// 实现vue文件中
window.addEventListener('message', (e) => {
if (e.origin !== window.location.origin && e.data !== 'pageshowEvent') return;
// 后续处理
}, false);
网友评论