1. 程式人生 > >scrollTop記錄滾動位置

scrollTop記錄滾動位置

前文提到,需要記錄滾動位置,

vue-router提供了一個方法 scrollBehavior,但是這個方法必須要在history模式下才能使用,具體參考文件

使用的方式如下

const scrollBehavior = (to, from, savedPosition) => {
   if (savedPosition) {
   setTimeout(() => {
     window.scrollTo(savedPosition.x, savedPosition.y)
   }, 200)
   }
}

export default new VueRouter({
    mode: 'history',
    scrollBehavior,
    routes: [{....}]
}]


但是我採用的是hash模式,這個方法便不能採用了,只能採用其他的方案。

筆者的思路是給滾動事件新增監聽,在離開頁面的時候紀錄當時的scrollTop,在返回頁面的時候設定scrollTop值為離開的時候的紀錄值。

首先要拿到scrollTop的值,

由於存在相容性問題,在拿到scrollTop的值的時候需要考慮各種情況。網上也有各種解決方案

各瀏覽器下 scrollTop的差異
IE6/7/8:
對於沒有doctype宣告的頁面裡可以使用 document.body.scrollTop 來獲取 scrollTop高度 ;
對於有doctype宣告的頁面則可以使用 document.documentElement.scrollTop;


Safari:
safari 比較特別,有自己獲取scrollTop的函式 : window.pageYOffset ;
Firefox:
火狐等等相對標準些的瀏覽器就省心多了,直接用 document.documentElement.scrollTop ;

獲取scrollTop值
完美的獲取scrollTop 賦值短語 :
var scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
通過這句賦值就能在任何情況下獲得scrollTop 值。
仔細觀察這句賦值,你發現啥了沒??
沒錯, 就是 window.pageYOffset (Safari) 被放置在 || 的中間位置。
因為當數字0 與 undefine 進行 或運算時,系統預設返回最後一個值。即或運算中 0 == undefine ;
當頁面滾動條剛好在最頂端,即scrollTop值為 0 時。 IE 下 window.pageYOffset (Safari) 返回為 undefine ,此時將window.pageYOffset (Safari) 放在或運算最後面時, scrollTop 返回 undefine , undefine 用在接下去的運算就會報錯咯。
而其他瀏覽器 無論 scrollTop 賦值或運算順序如何都不會返回 undefine. 可以安全使用.. 所以說到頭還是IE的問題咯. 

documentElement 和 body 相關說明:
body是DOM物件裡的body子節點,即 <body> 標籤;
documentElement 是整個節點樹的根節點root,即<html> 標籤;
DOM把層次中的每一個物件都稱之為節點,就是一個層次結構,你可以理解為一個樹形結構,就像我們的目錄一樣,一個根目錄,根目錄下有子目錄,子目錄下還有子目錄。
以HTML超文字標記語言為例:整個文件的一個根就是,在DOM中可以使用document.documentElement來訪問它,它就是整個節點樹的根節點。而body是子節點,要訪問到body標籤,在指令碼中應該寫:document.body。

為scroll事件新增監聽

為了監聽到scroll事件並進行操作,需要給scroll事件新增監聽函式。

addEventListener()與removeEventListener()用於處理指定和刪除事件處理程式操作。

它們都接受3個引數:如 addEventListener("事件名" , "事件處理函式" , "布林值"); (注:事件名不含"on",如“click”)

現在的版本可以省略第三個引數,預設值為false

需要注意的是,在addEventListener()的時候傳入的函式不能是匿名函式,不然的話在remove的時候並不會去除監聽。

筆者採用vue的自定義指令來完成。在定義指令的時候有兩個鉤子函式,bind和unbind,分別在這兩個函式內部進行監聽和移除監聽。

bind: function (el, binding, vnode, oldVnode) {
  document.addEventListener('scroll', scrollHandler)
},
unbind: function (el, binding, vnode, oldVnode) {
  document.removeEventListener('scroll', scrollHandler)
}

之後,需要設定scrollTop的位置了, 

首先拿到儲存的scrollTop值,筆者是存在sessionStorage裡的。

var scrollTop = window.sessionStorage.getItem('topSave')

直接將拿到的值賦給document.documentElement.scrollTop時發現跟預想的結果不一樣,打印出scrollTop的值發現,中間會有突變,猜測應該是瀏覽器內部也對scrollTop的值進行了設定,因此採用迂迴的方式,設定延遲,將值賦給相應的scrollTop,具體程式碼如下

setTimeout(function () {
  if (scrollTop) {
     document.documentElement.scrollTop = scrollTop
     window.pageYOffset = scrollTop
     document.body.scrollTop = scrollTop
  }
}, 3500)

採用setTimeout的方式,防止設定的時候還沒載入完,其中,scrollTop是存在sessionStorage裡的,將這個函式放在bind裡,保證只執行一次。

完成這個操作後,在chrome瀏覽器和安卓裝置上都可以完成恢復到記錄位置的功能。

但是setTimeout的問題是,1)卡頓,具體的表現是,回到列表頁之後到恢復滾動位置有一定的時間間隔,在這個時間內使用者可能已經有了其他的操作,突然回到一個位置導致卡頓;2)不起作用,具體的表現是,頁面的資料需要網路獲取,但是當資料返回時尚未渲染的時候已經完成了賦值操作,導致回到頂部無效。針對這兩個問題可以採取的解決方案有: 將賦值的操作放到document.ready()裡,參見文章

測試發現問題

1)在iOS裝置上卻沒有效果。始終回到頁面頂部,即scrollTop始終為0       

2)  當滾動距離超過一定的大小之後,無論儲存的scrollTop為多大的值,在設定完之後,scrollTop始終被設定為固定的一個值(筆者這邊是591)

針對這兩個問題的解釋,參見文章,摘錄如下

從詳情頁返回到列表頁面, 列表會重新渲染, 時序大概是這樣:
返回列表頁 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);
}
}

文章給了針對iOS多頁面混用一個scroll事件處理的解決方案

具體的思路是在
1、在每個需要用vue-router切換的元件的mounted鉤子內將頁面的位置自動回滾到頁面頂部,解決滾動條位置自動記錄問題;
2、在每個元件內定義一條變數scrollWatch預設為true,在繫結滾動監聽事件時加個if判斷,只有在scrollWatch為true時進行

監聽函式,然後在元件destroyed的鉤子內將變數scrollWatch設為false;這樣就解決了滾動監聽在別的元件內仍會執行的問題。

參考程式碼如下:

<script>
import $ from 'jquery';
export default {
data () {
return {
scrollWatch: true
}
},
mounted() {
$(window).scrollTop(0);
$(window).on('scroll', () => {
if (this.scrollWatch) {
//your code here
}
}
});
},
destroyed () {
this.scrollWatch = false;
}
}
</script>

2017.11.6

以上就是關於記錄滾動位置的解決方案。一些只是思路,嘗試沒有起作用,需要後期再思考完善,之後再附。。

參考網址

[1] http://www.cnblogs.com/xwgli/p/3490466.html

[2] http://blog.csdn.net/qq_29606781/article/details/67650869

[3] https://cn.vuejs.org/v2/guide/custom-directive.html

[4] https://router.vuejs.org/zh-cn/advanced/scroll-behavior.html

[5] http://www.jb51.net/article/118592.htm

[6] http://wowtianwen.iteye.com/blog/2100913

[7] http://www.jb51.net/article/107864.htm

相關推薦

scrollTop記錄滾動位置

前文提到,需要記錄滾動位置, vue-router提供了一個方法 scrollBehavior,但是這個方法必須要在history模式下才能使用,具體參考文件 使用的方式如下 const scroll

vue-scroller記錄滾動位置

ott als col posit rip outer -s func 切換 問題描述:   列表頁進入詳情頁,或者tab頁切換,然後再返回列表頁,希望能切換到之前滾動位置 解決問題思路:   切換到其他頁面前記錄位置,返回列表頁的時候返回位置。這就需要借助vue-rout

webview記錄滾動位置,下次開啟滾動記錄

網上看了挺多方法,自己都試了試發現不好用。 最後參考的是這篇文章。 http://blog.csdn.net/qiushi_1990/article/details/51849890       這

document.documentElement.scrollTop(獲取滾動位置)

而不是 cli 使用 位置 w3c scrolltop 標簽 如果 htm 要獲取當前頁面的滾動條縱坐標位置,用:document.documentElement.scrollTop;而不是:document.body.scrollTop;documentElement 對

extjs grid 實時重新整理的時候記錄滾動條的位置,讓滾動條不動

在實時重新整理的時候資料量過大會出現滾動條。這時候需要記錄滾動條的位置。只需在grid的viewConfig屬性裡面新增如下引數 viewConfig: {//實時重新整理的時候記錄滾動條的位置,讓滾動條不動     onLoad : Ext.emptyFn,       

Android--記錄和恢復listView滾動位置的3種方法

有時在view切換時,需要恢復listView之前滾動到的位置,常用的三種方法如下: 1、記錄listView滾動到的位置的座標,然後利用listView.scrollTo精確的進行恢復 listView.setOnScrollListener(new OnScrollListener()

document.documentElement.scrollTop(獲取滾動位置) .

要獲取當前頁面的滾動條縱座標位置,用: document.documentElement.scrollTop; 而不是: document.body.scrollTop; documentElement 對應的是 html 標籤,而 body 對應的是 body 標籤。 在標準w3c下,document.bo

測試scrollTop--隨滾動條距離頂部的高度body顯示不同的顏色

javascript<!doctype html> <html> <head> <title>測試scrollTop--隨滾動條距離頂部的高度body顯示不同的顏色</title> <meta name="author" con

網頁當前滾動位置

位置 div 當前 瀏覽器兼容 document nbsp spa 瀏覽器 scrolltop 瀏覽器兼容問題會導致document.body.scrollTop或者document.documentElement.scrollTop永遠為0,那我們做個判斷,然後賦值就可以

移動端彈性滑動以及vue記錄滑動位置

我們 移除 顯示 lis 滑動 手指 oca 方式 -i -webkit-overflow-scrolling介紹 -webkit-overflow-scrolling: auto | touch; auto: 普通滾動,當手指從觸摸屏上移開,滾動立即停止 touch:滾動

在JavaScript中獲取當前頁面的滾動位置

scrollTop要獲取當前頁面的滾動條縱坐標位置,用: document.documentElement.scrollTop; 而不是: document.body.scrollTop; documentElement 對應的是 html 標簽,而 body 對應的是 body 標簽。 documentEl

【Delphi】FMX 如何設定 TVertScrollBox的滾動位置,使其回到頂部原點

       在手機APP開發中, 縱向的滾動框非常實用,可以說是必不可少的控制元件,甚至為了自適應不同手機螢幕大小,使用滾動框是非常方便的懶人方法。        當我們使用TVertScrollBox控制元件時,發現除了控制

vue快取頁面返回到指定滾動位置

vue快取頁面返回到指定滾動位置 vue 中註冊滾動事件與dom 並無不同 以下配合keep-alive 元件使用 在 mounted 註冊滾動事件  1 this.handleScroll 獲取scrollTop

vue單頁應用中 返回列表記住上次滾動位置、keep-alive快取之後更新列表資料 那點事

實踐場景需求 產品列表中,滾動到一定位置的時候,點選檢視產品資訊,後退之後,需要回到原先的滾動位置,這是常見的需求 所有頁面均在router-view中,暫時使用了keep-alive來快取所有頁面,所以進入不同分類的產品列表,和不同的產品詳情頁面,需要更新資料 首先注意: 本次實踐測試環境為pc

React+React Router+React-Transition-Group實現頁面左右滑動+滾動位置記憶

在React Router中,想要做基於路由的左右滑動,我們首先得搞清楚當發生路由跳轉的時候到底發生了什麼,和路由動畫的原理。   首先我們要先了解一個概念:history。history原本是內置於瀏覽器內的一個物件,包含了一些關於歷史記錄的一些資訊,但本文要說的history是React-R

react判斷滾動到底部以及保持原來的滾動位置

這裡解決兩個問題: * 判斷某個元件是否滾動到底部 * 頁面切換出去再切換回來後怎樣保持之前的滾動位置 要保證這個元件就是那個滾動的元件,overflowY為scroll 判斷某

290. Word Pattern(技巧:記錄當前位置來判斷兩個字串是否符合)

Given a pattern and a string str, find if str follows the same pattern. Here follow means a full match, such that there is a bijection

vue中 進入詳情頁記住滾動位置(keep-alive)

vue中 進入列表詳情計算滾動位置 > 有時業務提出這樣一個需求 就是從商品頁面進入到列表詳情頁 要儲存當前滾動的位置,這裡我就想到了keep-alive 首先在路由中引入需要的模組 { path: ‘/scrollDemo’,

ASP.NET小技巧——回傳後保持頁面的滾動位置

預設情況下,ASP.NET頁面回傳到伺服器後,頁面會跳回頂部。 對於一個內容較多的頁面,你或許有需要自動滾動到使用者最後操作的位置。 頁面屬性 MaintainScrollPositionOnPostBack 可以解決這樣的問題,有以下3種方法。 應用程式級作用物件:網

vue-router如何在返回時返回到上次滾動位置 方法集錦

// list頁route中的data鉤子 route : { data : function () { var _this = this; // 返回同一個位置 var scrollTop = sessionStorage.getItem("scrol