问题由来
- 记上周做了一个微信
H5
的落地页,由于只有一个页面,而且全是图拼接而成的,所有采取了原始的jQuery
写法 - 内部有一些点击之后跳转到外部链接的需求,于是咔咔写下 :
$('xxx').click(function(){
localtion.href = url // 有埋点,不使用a链接
})
- 然后返回页面的时候,有趣的事情发生了!
微信内置浏览器在返回上一页面,且上一页面包含AJAX代码时(埋点需要AJAX)
IOS
默认是不进行页面刷新的,但android
页面就会被强制刷新
- 那么就会出现两种情况,
ios
微信返回上一页的原位置;而Android
直接定位到了顶部,弱化了用户体验
尝试解决
- 让
Android
在返回页面后定位到原来位置
方案①滚动定位
- 跳转之前记录当前页面的滚动位置,存到
sessionStorage
里面
$.ajax({
type:'POST',
url:`${baseUrl}/app/sta`,
data:{ requestId,type},
success(res){
if(res.code===200){
// 跳转之前先记录当前的滚动位置
sessionStorage.setItem('scrollTop',$('.wrapper').scrollTop());
location.href = hrefUrl
}else{
clearTimeout(timer)
$('.model').stop().fadeIn(1000)
timer = setTimeout(() => {
$('.model').stop().fadeOut(700)
}, 2000);
}
}
})
- 在返回的时候判断是否滚动的位置是否存在,然后直接滚动到对应位置,最后清除
sessionStorage
const u = navigator.userAgent
const isAndroid = u.includes('Android') || u.includes('android') || u.includes('Adr')
// 如果是安卓设备,且保存了滚动位置
if(isAndroid && sessionStorage.getItem('scrollTop')){
$('.wrapper').scrollTop(Number(sessionStorage.getItem('scrollTop')));
// 清除上次缓存
sessionStorage.removeItem('scrollTop');
}
- 然后发现这个做法定位根本不准确,页面每次点击去总会往上跑,有时候甚至相差很大(几百
px
)
- 因为此时图片的父盒子高度是由图片撑开的,而图片又做了懒加载,
- 然后我想是不是页面没给具体高度,返回时页面未加载完成,无法获取具体高度,所以导致定位不准
- 随后给根标签设置了一个
height:4091px
,让html
滚动,在浏览器确实获取到了document.documentElement.scrollTop
,但是打开手机的调试工具vconsloe
,发现document.documentElement.scrollTop
始终为0,而且document.documentElement
是个空对象 - 而且给跟标签设置了高度,首次进入页面会发现滑动不下去,多滑动几次才行
方案②锚点定位
- 采取锚点定位的方法,跳转之前先记录点击的锚点
$('.reserve').click(function(){
sessionStorage.setItem('anchor', '#knowledge');
...
})
})
- 返回页面时检测是否存在锚点,然后跳转到锚点位置
if(isAndroid){
location.href = sessionStorage.getItem('anchor');
// 清除上次缓存
sessionStorage.removeItem('anchor');
}
- 这样做虽然不是定位到原位置,但是返回之后能让用户看到点击的区域,也算是优化体验
- 但是这样相当于返回原页面之后做了一次跳转
- 从 http://127.0.0.1:5502/index.html 跳转到 http://127.0.0.1:5502/index.html#knowledge
- 于是浏览器存在了两条历史记录,安卓设备点击物理返回键的时候,会从带
#knowledge
的页面返回不带的页面,相当于需要点击两次才能退出页面,存在问题
方案③scrollIntoView
- 滚动元素的父容器,使被调用
scrollIntoView()
的元素对用户可见 - 效果和锚点差不多,只是不会浏览器不会产生记录
- 点击前仍要保存点击位置的锚点
if(isAndroid){
$(sessionStorage.getItem('anchor'))[0].scrollIntoView()
// 清除上次缓存
sessionStorage.removeItem('anchor');
}
可选参数
-
alignToTop
(布尔值):- 如果为
true
,元素的顶端将和其所在滚动区的可视区域的顶端对齐 - 如果为
false
,元素的底端将和其所在滚动区的可视区域的底端对齐
- 如果为
-
scrollIntoViewOptions
(对象):- behavior: 定义动画过渡效果,
auto
或smooth
之一,默认为auto
- block: 定义垂直方向的对齐,
start
,center
,end
,或nearest
之一,默认为start
- inline: 定义水平方向的对齐,
start
,center
,end
,或nearest
之一,默认为nearest
- behavior: 定义动画过渡效果,
注意:
alignToTop
为true
相当于scrollIntoViewOptions:{block:"start", inline:"nearest"}
alignToTop
为false
相当于scrollIntoViewOptions:{block: "end", inline: "nearest"}
兼容性
- 苹果浏览器不兼容,但是不需要考虑
判断页面加载类型
- 定位问题已经解决,但是还有一个问题!
- 如果用户跳到外部链接后,直接把整个页面或者微信关掉,而不是返回首页,那么记录的锚点就不会被清除
- 那么下次进入页面的时候,就会直接跳转到对应的位置,存在
bug
- 这时就需要做一次判断,判断是以什么方式进入页面的 (直接进入 or 回退)
$(document).ready(function (){
window.historyPageMark = false; // 标记是否为历史页面
// 监听pageshow事件,页面显示则触发
function onPageBack(callback){
$(window).on("pageshow",e => {
if(window.historyPageMark === true || e.persisted || isEnterByBack()){
// 如果是历史页面,则触发回调
callback();
}
window.historyPageMark = true; // 首次pageshow执行后更新标记为历史页面
})
}
function isEnterByBack(){ // 判断页面进入类型是否为回退
if(!window.performance) return false;
if(window.performance.getEntriesByType("navigation")[0]){
return window.performance.getEntriesByType("navigation")[0].type === "back_forward";
}
if(window.performance.navigation){
return window.performance.navigation.type === 2;
}
return false;
}
})
-
window.performance: 用于测量网页和 Web 应用程序的性能
-
window.performance.getEntriesByType(): 返回一个
PerformanceEntry
对象的数组 -
window.performance.getEntriesByType("navigation")[0] :一般用于监控网页的性能
- 其中type属性有两个值,
reload
表示重载,back_forward
表示回退
- 其中type属性有两个值,