解决Vue页面固定滚动位置的处理办法
最近做项目遇到一个问题,就是Vue滚动不固定,网上找了一些资料,说下vue固定滚动位置的处理办法.
问题描述:
通常见于列表页List->详情页Detail的情况,从列表的某一项x进入到详情页,再返回的时候,希望列表的位置固定在x,而不是回到顶部了.
vue-router里面是有一个scrollBehavior的,但是这个玩意只能在history模式下面使用,而我用的hash模式.
所以我们要自己实现嘛,思路简单:List里面监听滚动,记录滚动位置pos,从Detail返回到List里面的时候,读取pos.
mounted(){ //读 setTimeut(function(){ document.body.scrollTop=parseInt(sessionStorage.getItem('pos')); },1000); //存 window.onscroll=function(){ sessionStorage.setItem('pos',document.body.scrollTop); } }
遇见了一个问题:
每次返回List,都是直接滚动到顶部,每次都是,每次都是!把pos打印出来,发现是0,而不是我们所存的值.日了,明明切换之前还是的,回来就不是了.
然后发现了路由每次切换都会触发onscroll事件,日了狗,为毛.我都没有滚动页面,为什么会触发onscroll事件。
刚开始怀疑hash变化会导致onscroll事件的触发,所以我就在浏览器里面手动输入了几个不存在的路由:
/foo /bar
没有发现scroll被触发,所以这个嫌疑排出.
然后怀疑vue-router里面是不是绑定了scroll事件,没发现然后又想,没绑定scroll事件,那么修改scrollTop值会不会也触发scroll事件.
好吧还发现新知识点了:
scrollTop值的改变,的确会触发scroll事件.
那么我就想,是不是vue-router里面存在修改scrollTop值的行为,也没有发现.
然后我又想,数据是动态渲染的,所以是不是和元素的增删改查相关。
元素增加->页面高度变了->页面高度变化,也触发scroll事件?
所以我用vue-cli新建了项目,放了两个没有增删改查的路由
然后日了狗的,我看见从foo->bar->foo,的时候,foo的滚动条位置还在之前我滚动到的地方.
突然想起来浏览器是可以自己记录滚动条位置的.
是不是浏览器干的?
从详情页返回到列表页面,列表会重新渲染,时序大概是这样:
返回列表页1
渲染页面 2
而浏览器恢复滚动条的位置的操作,是在1和2之间,这个时候就出问题了:如果你页面上面的数据都是渲染出来的,浏览器就会发现:
页面的高度<=屏幕的高度,不存在滚动条,此时document.body.scrollTop=0;
所以会设置document.body.scrollTop=0
修改了document.body.scrollTop触发了scroll事件,scroll里面又重写了pos
等你数据渲染结束之后,读到的就是0了.
如果发现你页面高度大于屏幕高度,但是页面高度是n,而pos的值是:n+x,比当前页面的最大的scrollTop值还大,这个时候,document.body.scrollTop的值就会等于n.
当你的数据渲染结束,开始定位,日了,没定准.
所以我们要解决这个问题.
当然是想到了keep-alive,刚启用的时候,发现的确不错.但是同时也发现:
列表项目靠前的,往返操作的定位都很准,越往后越不行,直接拉到底,再返回发现定位到的一般都是第二个第三个列表项目.
所以这个就很有意思了,我大概猜测了一下浏览器的滚动位置恢复行为:
当hashchange的时候。拿到当前页面的document.body.scrollTop值,和自己存储的滚动条位置。二者取最小的值,设置成当前的document.body.scrollTop的值,当使用keep-alive的时候,因为hashchange事件处理和页面渲染是并行的,所以有时hashchange拿到的document的高度是已经渲染过几个元素的高度,这个就是为什么定不准的原因.
好吧,现在的情况是:
keep-alive定不准,不可靠,所以需要我们自己来重新定位.
ok,1先绑定scroll事件:
varmap={}; window.onscroll=function(){ map[location.hash]=document.body.scrollTop; }
2再屏蔽掉浏览器自动恢复滚动位置行为带来的影响
a在hashchange时强制document.body.scrollTop=0
b在scroll事件里面,当document.body.scrollTop=0的时候不做存操作.
varmap={}; window.onhashchange=function(){ document.body.scrollTop=0; } window.onscroll=function(){ if(document.body.scrollTop){ //存 map[location.hash]=document.body.scrollTop; }else{ //读 } }
3在读操作里面,设置一个定时任务,去判断document.body.scrollTop的值和你保存的位置是不是相同的
varmap={}; window.onhashchange=function(){ document.body.scrollTop=0; } window.onscroll=function(){ if(document.body.scrollTop){ //存 map[location.hash]=document.body.scrollTop; }else{ vartimer=null; timer=setInterval(function(){ if(document.body.scrollTop==map[location.hash]){ clearInterval(timer); }else{ document.body.scrollTop=map[location.hash]; } },20); } }
到这里实际上已经大体实现了,返回恢复滚动条位置的功能,而上面的代码需要更多的优化,
具体代码见:项目地址
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。