前のページのスクロールした位置に戻れない時の条件
- 前のページに戻った際にheightに変動のあるDOM構築を行っている場合
- 現在のページのスクロール可能領域が前のページのスクロール可能領域よりも少なく、前のページのスクロール位置が現在のページのスクロール可能領域の最大よりも大きかった場合 ※本当にコレが原因かは不明。
対処法
DOM構築が終わった後にscrollTo()
で前のページのスクロール位置まで移動させる。
実装方法
前のスクロール位置の情報をsessionStorageに持たせる
Routerに以下を追加
scrollBehavior(to, from, savedPosition) { if (savedPosition) { sessionStorage.setItem('positionY', savedPosition.y); return savedPosition } else { sessionStorage.setItem('positionY', 0); return { x: 0, y: 0 } } }
scrollBehaviorを使うとページを戻ったタイミングで、前のスクロール位置の情報取れるので前のスクロール位置の情報をsessionStorageに持たせる。
今回は横スクロールはなかったので縦軸の値だけ。
scrollBehaviorについて
前のページに戻って、そのページのDOM構築が終わったタイミングで、sessionStorageに保存した、スクロール位置の情報をscrollTo()の引数に渡して、スクロールする処理を入れる。
前のページのコンポーネントに以下を追加
watch: { hogeList: function() { this.$nextTick(() => { var positionY = sessionStorage.getItem('positionY') scrollTo(0, positionY); setTimeout(function(){ scrollTo(0, positionY); }, 500); }) } },
今回はbeforeCreateでdispatchしていて、computedでstateの値をreturnしています。(詳しくはvuexのドキュメントを読んで)
なので、上の例ではhogeListの値が変わったタイミングで実行されるwatch内で処理しています。DOMが更新されたタイミングで発火するようにnextTickを使っています。
nextTickについて
ただDOM構築が終わっていても画像のロードが終わっていない場合があり、その場合、nextTickの後にもheightが変わる可能性があり、nextTickを使ってscrollTo()をしてもスクロール位置がずれてしまう場合があります。
そこで画像のwidthを100%などにしなければならなくheightを固定出来ない場合などは、setTimeoutを使いscrollTo()の実行タイミングを画像のロードが終るタイミングで実行するようにします。
nextTickについて
nextTickは実行時点から次のキューイングが実行されDOMが更新されたタイミングでコールバック関数が呼び出されます。
(Vue.jsでビューの変更がされないときに疑うこと+主な解決策方法より引用)
scrollBehaviorについて
- ページ遷移時に発火します。
- 前のページに戻った時にsavedPositionに前のページのスクロールした位置がオブジェクトで返ってくる。例 { x: 1234, y: 5678 }
- returnしたら、その箇所までスクロールするのかと思ったのですが、そうではないようです。 returnした値をどこで取得できるかは分かりませんでした。
https://router.vuejs.org/ja/guide/advanced/scroll-behavior.html
その他vue-routerを使う上での注意点
注意: ルートの行き先が現在のルートと同じで、かつパラメータのみが変更されている場合(例: /users/1 -> /users/2 のようにあるプロファイルから他へ)、変更(例: ユーザー情報の取得など)に反応するためにbeforeRouteUpdate を使用しなければなりません。
(https://router.vuejs.org/ja/guide/essentials/navigation.html より引用)
まとめ
sessionStorageで値を持ってscrollTo()を使って無理やりスクロールの位置を合わせているので、あまり良い実装とは言えないです。
scrollBehaviorを使ったりしてもっといい感じに実装できるよって方いれば教えてください。