解决Vue页面固定滚动位置的处理办法


Posted in Javascript onJuly 13, 2017

最近做项目遇到一个问题,就是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 事件:

var map = {};
 window.onscroll = function() {
  map[location.hash] = document.body.scrollTop;
 }

2 再屏蔽掉浏览器自动恢复滚动位置行为带来的影响

a 在 hashchange 时强制 document.body.scrollTop = 0

b 在 scroll 事件里面, 当 document.body.scrollTop = 0 的时候不做 存操作.

var map = {};
 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 的值和你保存的位置是不是相同的

var map = {};
 window.onhashchange = function() {
  document.body.scrollTop = 0;
 }
 window.onscroll = function() {
  if (document.body.scrollTop) {
   // 存
   map[location.hash] = document.body.scrollTop;
  } else {
   var timer = null;
   timer = setInterval(function(){
    if (document.body.scrollTop == map[location.hash]) {
     clearInterval(timer);
    } else {
     document.body.scrollTop = map[location.hash];
    }
   }, 20);
  }
 }

到这里实际上已经大体实现了, 返回恢复滚动条位置的功能, 而上面的代码需要更多的优化,

具体代码见:项目地址

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
使用jQuery的将桌面应用程序引入浏览器
Nov 19 Javascript
Javascript自定义排序 node运行 实例
Jun 05 Javascript
Js放到HTML文件中的哪个位置有什么区别
Aug 21 Javascript
javascript Array 数组常用方法
Apr 05 Javascript
javascript中加var和不加var的区别 你真的懂吗
Jan 06 Javascript
JavaScript+html5 canvas绘制渐变区域完整实例
Jan 26 Javascript
微信小程序 页面之间传参实例详解
Jan 13 Javascript
AngularJS 验证码60秒倒计时功能的实现
Jun 05 Javascript
JavaScript中this的全面解析及常见实例
May 14 Javascript
layui实现tab的添加拒绝重复的方法
Sep 04 Javascript
vue+element搭建后台小总结 el-dropdown下拉功能
Apr 10 Javascript
JavaScript实现拖拽效果
Mar 16 Javascript
vue+ElementUI实现订单页动态添加产品数据效果实例代码
Jul 13 #Javascript
Angular如何引入第三方库的方法详解
Jul 13 #Javascript
详解如何构建Angular项目目录结构
Jul 13 #Javascript
深入理解Vue-cli搭建项目后的目录结构探秘
Jul 13 #Javascript
JS实现弹出下载对话框及常见文件类型的下载
Jul 13 #Javascript
vue开发调试神器vue-devtools使用详解
Jul 13 #Javascript
基于JS代码实现简单易用的倒计时 x 天 x 时 x 分 x 秒效果
Jul 13 #Javascript
You might like
点评山进PR-D3L三波段收音机
2021/03/02 无线电
php写的AES加密解密类分享
2014/06/20 PHP
PHP以mysqli方式连接类完整代码实例
2014/07/15 PHP
PHP读取大文件的多种方法介绍
2016/04/04 PHP
thinkPHP5.0框架自动加载机制分析
2017/03/18 PHP
YII框架常用技巧总结
2019/04/27 PHP
laravel 解决后端无法获取到前端Post过来的值问题
2019/10/22 PHP
用js 让图片在 div或dl里 居中,底部对齐
2008/01/21 Javascript
Jquery的hide及toggle方法让超链接慢慢消失
2013/09/06 Javascript
Jquery使用Firefox FireBug插件调试Ajax步骤讲解
2013/12/02 Javascript
JavaScript学习笔记之定时器
2015/01/22 Javascript
jQuery判断数组是否包含了指定的元素
2015/03/10 Javascript
jQuery 实现评论等级好评差评特效
2016/05/06 Javascript
JavaScript注入漏洞的原理及防范(详解)
2016/12/04 Javascript
jquery 校验中国身份证号码实例详解
2017/04/11 jQuery
给Easyui-Datebox设置隐藏或者不可用的解决方法
2017/05/26 Javascript
带你了解session和cookie作用原理区别和用法
2017/08/14 Javascript
详解 微信小程序开发框架(MINA)
2019/05/17 Javascript
js实现滑动滑块验证登录
2020/07/24 Javascript
[01:04:48]VGJ.S vs TNC Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
Python 比较两个数组的元素的异同方法
2017/08/17 Python
Python编程之基于概率论的分类方法:朴素贝叶斯
2017/11/11 Python
python DataFrame 修改列的顺序实例
2018/04/10 Python
解决pandas.DataFrame.fillna 填充Nan失败的问题
2018/11/06 Python
python 读取dicom文件,生成info.txt和raw文件的方法
2019/01/24 Python
Python进阶之使用selenium爬取淘宝商品信息功能示例
2019/09/16 Python
python 如何实现遗传算法
2020/09/22 Python
Tory Burch美国官方网站:美国时尚生活品牌
2016/08/01 全球购物
奥地利度假券的专家:we-are.travel
2019/04/10 全球购物
最新会计专业求职信范文
2014/01/28 职场文书
作文评语集锦
2014/12/25 职场文书
学习杨善洲同志先进事迹心得体会
2016/01/23 职场文书
创业计划书之面包店
2019/09/17 职场文书
导游词之云南丽江古城
2019/09/17 职场文书
Nginx+Tomcat实现负载均衡、动静分离的原理解析
2021/03/31 Servers
Nginx如何配置根据路径转发详解
2022/07/23 Servers