Vue.jsを使って、Webサイトを作成していた際に、他の画面に遷移して元の画面に戻ってくると、スクロール位置がページトップに戻ってしまう…
そこで、ブラウザバック時にスクロール位置を元に戻す実装方法について。ライブラリ等は使わずに、実装していきます!
Vue.jsでブラウザバック時にスクロール位置を元に戻す
他ページ遷移前に、垂直方向にスクロールしたpx数をsessionStorageに保存
他ページに遷移する前に、垂直方向にスクロールしたpx数をwindow.scrollYで取得し、sessionStorageに保存します。
export default {
mounted() {
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("scrollY", window.scrollY);
});
}
}
beforeunloadは、ページ移動前に実施されるイベントです。例えば、外部リンクを開く前などに実施されます。
sessionStorageは、ページのセッションが続いている間、データを保存できる領域。似たようなものとして、localStorageがあります。
- sessionStorage
- タブやウィンドウを閉じると、データが破棄される
- localStorage
- データの保存期限がなく、タブやウィンドウを閉じてもデータが保持される
元のページに戻ってきた際に、指定の座標までスクロールさせる
戻るまたは進むアクションを行った後に、元のページに戻ってきた場合、指定の座標までスクロールさせます。
export default {
mounted() {
(() => {
let entries = performance.getEntriesByType("navigation");
entries.forEach((entry) => {
if (entry.type == "back_forward" && sessionStorage.getItem("scrollY") != null) {
window.scrollTo(0, sessionStorage.getItem("scrollY"));
}
});
})();
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("scrollY", window.scrollY);
});
}
}
戻るもしくは進むアクションを行なったかどうかをページ読み込み時に判断する際に、PerformanceNavigationTimingインターフェースを使います。
ナビゲーションの種類には、「navigate・reload・back_forward・prerender」がありますが、今回は、back_forwardを指定。
リファレンス:PerformanceNavigationTiming.type – Web APIs | MDN
戻るもしくは進むアクションを行い、scrollYキーがnullではない場合に、元の位置までスクロールさせています!
動的表示させている箇所がある場合、スクロール位置がずれてしまう
静的に表示させている箇所しかない場合は、上記に記載した方法で問題ありませんが、APIを叩いて情報を取得・表示させている箇所などは、その分スクロール位置がずれてしまいます。
そこで、DOMが更新された後に元の位置までスクロールさせるようにします。
export default {
mounted() {
this.getItem().then((response) => {
// 取得したアイテムを変数に入れたりする何かしらの処理
// DOM更新後に、スクロール位置を移動するようにする
this.$nextTick(() => {
let entries = performance.getEntriesByType("navigation");
entries.forEach((entry) => {
if (entry.type == "back_forward" && sessionStorage.getItem("scrollY") != null) {
window.scrollTo(0, sessionStorage.getItem("scrollY"));
}
});
});
}).catch((e) => {
console.log(e);
});
window.addEventListener("beforeunload", () => {
sessionStorage.setItem("scrollY", window.scrollY);
});
},
method: {
async getItem() {
// アイテムを取得する何かしらの処理
}
}
}
DOM更新完了後に、処理を実行する $nextTick()を使用しています。
callback を延期し、DOM の更新サイクル後に実行します。DOM 更新を待ち受けるために、いくつかのデータを変更した直後に使用してください。
API — Vue.js
これで、動的に表示させている箇所があった場合でも、ブラウザバックした際にスクロール位置がずれることはなくなりました!
safariで開いた場合に上述したやり方では上手くいかない
上述した実装方法で、問題なく元のスクロール位置に戻ると思ったのですが、safariでは、元のスクロール位置に戻りませんでした。
原因は、おそらくbfcacheなるものが効いているため、Chrome等のブラウザと挙動が変わってくるそう。
そこで、HistoryインターフェースのscrollRestorationプロパティを使って、スクロール位置を復元させる方法にしました。
mounted() {
if ("scrollRestoration" in window.history) {
window.addEventListener('load', () => {
window.history.scrollRestoration = 'auto';
});
}
}
これだけでOK!
sessionStorageや$nextTick()を使わずとも、この記述だけで、ブラウザバックした際に、safariの場合でも、元のスクロール位置に戻るようになりました。
【補足】URLに#(ハッシュ)がついている場合
URLにハッシュがついている場合があるとします。例えば以下のような感じ。
https://hoge.com/#id
通常であれば、上記のURLにアクセスすると、ハッシュに指定したidの箇所にスクロールされますが、内部に動的に表示している箇所(APIから取得している箇所等)がある場合に、若干ずれてしまいます。
そこで、下記のようにscrollIntoView()を使用することにより、正常に動作するようになりました。
mounted() {
if ("scrollRestoration" in window.history) {
window.addEventListener('load', () => {
const hash = this.$route.hash;
if (hash) {
document.getElementById(hash.replace("#", "").scrollIntoView();
} else {
window.history.scrollRestoration = 'auto';
}
});
}
}
以上、最後まで読んでいただきありがとうございました!
コメント