• #Vue
  • #UX
  • #scrollIntoView
  • #キーボードナビゲーション
開発メモ

Vueリストの自動スクロール実装 - scrollIntoViewのblock: centerで先読み表示

結論

リストで矢印キー移動時に選択アイテムを自動スクロールするにはscrollIntoView({ block: 'center' })を使う。block: 'nearest'だと画面端まで来ないとスクロールしないため、先のアイテムが見えずUXが悪い。

watch(() => props.currentIndex, (newIndex) => {
  if (newIndex < 0 || !listRef.value) return
  nextTick(() => {
    const item = listRef.value?.querySelector(`[data-index="${newIndex}"]`)
    if (item) {
      item.scrollIntoView({ block: 'center', behavior: 'smooth' })
    }
  })
})

問題

ファイルリストで矢印キーを使って移動する際に、選択アイテムが画面外に出ても自動でスクロールしない問題があった。

スクロール問題のBefore - 11番目を選択中だが、先に何件あるか見通しが悪い

具体的には:

  • 38件のアイテムがあるリストで、20番目あたりを選択中
  • 下矢印キーで移動しても、38番目付近まで来ないとスクロールが発生しない
  • 先に何件あるのかがわからず、UXが悪い

最初の実装: block: 'nearest'

最初はscrollIntoView({ block: 'nearest' })で実装した。

item.scrollIntoView({ block: 'nearest', behavior: 'smooth' })

この実装の問題点:

  • アイテムが表示範囲内にあればスクロールしない
  • 画面の端ギリギリまで来て初めてスクロールが発生
  • 先のアイテムが見えないため、リストの終わりが予測できない

改善: block: 'center'

block: 'center'に変更すると、選択アイテムが常にリストの中央付近に表示される。

item.scrollIntoView({ block: 'center', behavior: 'smooth' })

メリット:

  • 選択アイテムの上下に同程度のアイテムが見える
  • 先に何件あるかが常に把握できる
  • リストの最初/最後付近では自然な位置に収まる

実装の全体像

テンプレート

<div ref="listRef" class="sidebar-list">
  <div
    v-for="(item, index) in items"
    :key="item.file_name"
    :data-index="index"
    class="sidebar-item"
    :class="{ active: index === currentIndex }"
    @click="emit('select', index)"
  >
    <!-- アイテムの内容 -->
  </div>
</div>

ポイント:

  • リストコンテナにref="listRef"を設定
  • 各アイテムに:data-index="index"を追加してDOM検索に使用

スクリプト

const listRef = ref<HTMLElement | null>(null)

watch(() => props.currentIndex, (newIndex) => {
  if (newIndex < 0 || !listRef.value) return
  nextTick(() => {
    const item = listRef.value?.querySelector(
      `[data-index="${newIndex}"]`
    ) as HTMLElement | null
    if (item) {
      item.scrollIntoView({ block: 'center', behavior: 'smooth' })
    }
  })
})

ポイント:

  • nextTick()でDOM更新後に実行
  • data-index属性でアイテムを特定
  • behavior: 'smooth'でスムーズなスクロール

Afterの動作例: スクロール問題のAfter - 11番目を選択中で、先に何件あるか見通しが良い

scrollIntoViewのblockオプション比較

オプション挙動ユースケース
'start'要素をコンテナの上端に配置ページトップへの移動
'center'要素をコンテナの中央に配置リストナビゲーション
'end'要素をコンテナの下端に配置チャットの新着メッセージ
'nearest'最小限のスクロールで表示既に見えている可能性が高い場合

まとめ

リストのキーボードナビゲーションではscrollIntoView({ block: 'center' })を使うと選択アイテムの前後が常に見えてUXが良い。block: 'nearest'は画面端まで来ないとスクロールしないため、先読みができず使いづらい。