解决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 相关文章推荐
根据表格中的某一列进行排序的javascript代码
Nov 29 Javascript
教你如何使用PHP输出中文JSON字符串
May 22 Javascript
require.js+vue开发微信上传图片组件
Oct 27 Javascript
Bootstrap CSS布局之代码
Dec 17 Javascript
基于JavaScript实现窗口拖动效果
Jan 18 Javascript
遍历json获得数据的几种方法小结
Jan 21 Javascript
jQuery实现获取隐藏div高度的方法示例
Feb 09 Javascript
AngularJS中filter的使用实例详解
Aug 25 Javascript
解决iView中时间控件选择的时间总是少一天的问题
Mar 15 Javascript
小程序视频或音频自定义可拖拽进度条的示例代码
Sep 30 Javascript
Vue基于vuex、axios拦截器实现loading效果及axios的安装配置
Apr 26 Javascript
使用zrender.js绘制体温单效果
Oct 31 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
redirect_uri参数错误的解决方法(必看)
2017/02/16 PHP
Laravel中encrypt和decrypt的实现方法
2017/09/24 PHP
Jquery Ajax学习实例6 向WebService发出请求,返回DataSet(XML) 异步调用
2010/03/18 Javascript
Jquery插件 easyUI属性汇总
2011/01/19 Javascript
jquery实现模拟百分比进度条渐变效果代码
2015/10/29 Javascript
JavaScript精炼之构造函数 Constructor及Constructor属性详解
2015/11/05 Javascript
JQuery核心函数是什么及使用方法介绍
2016/05/03 Javascript
改变checkbox默认选中状态及取值的实现代码
2016/05/26 Javascript
JSONP原理及简单实现
2016/06/08 Javascript
表单中单选框添加选项和移除选项
2016/07/04 Javascript
javaScript+turn.js实现图书翻页效果实例代码
2017/02/16 Javascript
AngularJS全局警告框实现方法示例
2017/05/18 Javascript
从源码看angular/material2 中 dialog模块的实现方法
2017/10/18 Javascript
使用vue-router在Vue页面之间传递数据的方法
2019/07/15 Javascript
js 实现ajax发送步骤过程详解
2019/07/25 Javascript
JS实现容器模块左右拖动效果
2020/01/14 Javascript
基于JavaScript实现控制下拉列表
2020/05/08 Javascript
[01:33:07]VGJ.T vs Newbee Supermajor 败者组 BO3 第一场 6.6
2018/06/07 DOTA
[51:27]LGD vs Liquid 2019国际邀请赛小组赛 BO2 第二场 8.16
2019/08/19 DOTA
python3爬取数据至mysql的方法
2018/06/26 Python
对pandas读取中文unicode的csv和添加行标题的方法详解
2018/12/12 Python
Python3.4学习笔记之常用操作符,条件分支和循环用法示例
2019/03/01 Python
python如何实现单链表的反转
2020/02/10 Python
Keras预训练的ImageNet模型实现分类操作
2020/07/07 Python
日本最新流行服饰网购:Nissen
2016/07/24 全球购物
南非领先的在线旅行社:Travelstart南非
2016/09/04 全球购物
英国最大的老式糖果店:A Quarter Of
2017/04/08 全球购物
波兰在线香水店:Perfumy.pl
2019/08/12 全球购物
毕业生自荐书
2013/12/18 职场文书
求职信模板标准格式范文
2014/02/23 职场文书
大学毕业生推荐信
2014/07/09 职场文书
考研英语复习计划
2015/01/19 职场文书
再读《皇帝的新衣》的读后感悟!
2019/08/07 职场文书
Python基础之赋值,浅拷贝,深拷贝的区别
2021/04/30 Python
JavaScript如何优化逻辑判断代码详解
2021/06/08 Javascript
HTML静态页面获取url参数和UserAgent的实现
2022/08/05 HTML / CSS