Vuetifyのタブコンポーネント
VuetifyはVue.jsに対応したUIコンポーネントライブラリです。今回はこの中のタブ(v-tabs
)に関するTipsです。
https://vuetifyjs.com/ja/components/tabs
タブごとにスクロール位置を保持したい
例えば、3つのタブを作り、その中にInstagramのタイムラインのようなコンテンツのリストを表示するとします。
このとき、次のような事象が発生します。
- タブAで
window.scrollY
が500になるまでスクロール - タブBに切り替え
- タブBでもスクロール位置(
window.scrollY
)が500になっている
感覚的には、タブAでwindow.scrollY=500
の位置までスクロールしても、タブBに切り替えたときには一番上(window.scrollY=0
)の位置を表示してほしいですよね。
方針
次のような方針で対応していきます。
- スクロールのイベントハンドラを追加する
- スクロールが発生したら、現在表示中のスクロール位置を保持する(タブごとに別々に保持)
-
v-tabs
のv-model
に指定したプロパティ(例: active)をwatchする -
active
の変更を検知したタイミングで、保持しておいたスクロール位置に変更する
実装
Javascriptの処理はこんな感じ。
sample.vue
<script>
export default {
data() {
active: 'tabA', // アクティブなタブ名
// 各タブのスクロール位置
position: {
tabA: null,
tabB: null,
tabC: null,
},
},
mounted() {
// イベントリスナの追加
window.addEventListener('scroll', this.handleScroll);
},
destroyed() {
// イベントリスナの削除
window.removeEventListener('scroll', this.handleScroll);
},
methods: {
handleScroll() {
// 現在アクティブなタブのスクロール位置を保持
this.position[this.active] = window.scrollY;
}
},
watch: {
// activeの変更を検知
active() {
// 切り替え後のタブですでに保持されたスクロール位置があればその位置を取得
const y = this.position[this.active] || 0;
// 即時スクロールすると、切り替え前のタブの長さ < 切り替え後のタブの長さである場合に、切り替え前のタブの最大値までしかスクロールされないことがある(切り替え後のタブの内容が描画される前にスクロールしようとする)ので、setTimeoutでタイミングを少しずらす
setTimeout(() => {
window.scroll(0, y);
}, 200);
},
},
};
</script>
終わりに
省略した<template>
の部分は基本的にVuetifyのサンプルを見てもらえばと思いますが、要望がありましたら追記します。