Jspreadsheet の横スクロールでブラウザバックされる問題をどう解決したか

はじめに

業務アプリケーションで、Excel ライクな操作感を求められる場面は少なくありません。
今回のプロジェクトでも、表形式での編集やコピー&ペーストといった要件から Jspreadsheet を利用していました。

実装自体は問題なく進んでいたのですが、実運用に入ってから
想定していなかった UX 上の問題が発生しました。

それが、横スクロール操作によってブラウザバックが発生してしまう問題です。

本記事では、

  • 実際に起きた問題
  • 原因の切り分け
  • 試したが解決しなかった方法
  • 最終的に採用した対策

を順に紹介します。


起きた問題:横スクロールで画面が戻る

Jspreadsheet では、列数が多い場合に横スクロール操作が発生します。
PC ではトラックパッド、Mac では 2 本指スワイプで操作することが多いでしょう。

しかし、ある画面で次のような問題が起きました。

  • 表を横にスクロールしようとする
  • 意図せずブラウザバックが発生する
  • 入力途中のデータが消える

特に 業務中の連続操作で頻発し、
ユーザー体験としては致命的な問題でした。


なぜ起きたのか

原因は、ブラウザの履歴ナビゲーションと横スワイプ操作の競合でした。

  • ブラウザ(特に macOS + Chrome / Safari)
  • トラックパッドの横スワイプ
  • 画面全体で有効な履歴操作

この状態で、Jspreadsheet の横スクロール操作が
ブラウザに「戻るジェスチャー」として解釈されてしまうと、
意図せず画面遷移が発生します。

Jspreadsheet 自体の不具合というより、
ブラウザ挙動と UI 構成の組み合わせによって起きた問題でした。


最初に試したがダメだった方法

① Jspreadsheet 側の設定で制御する

Jspreadsheet のオプションで解決できないか調べましたが、

  • スクロール挙動自体を制御する設定はない
  • ブラウザの履歴操作までは制御できない

という結論に至りました。


② CSS で overflow を調整する

overflow-x: auto;

といった対応も試しましたが、

  • 表の外側でスワイプされると意味がない
  • 根本的な解決にはならない

という問題が残りました。


③ 画面全体でイベントを止める

wheeltouch イベントを画面全体で preventDefault する案も検討しましたが、

  • 表以外の操作にも影響が出る
  • 他のコンポーネントと衝突しやすい
  • UX を大きく損なう可能性がある

ため、採用は見送りました。


最終的に採用した解決策

結論としては、

Jspreadsheet のスクロール操作に限定して、 ブラウザバックにつながる横方向のスクロール入力だけを抑止する

という方針を取りました。

ポイントは、制御のスコープをできるだけ限定することです。

  • 常に無効化しない
  • 表のスクロール操作中のみ制御する
  • 表の外の操作には影響させない

実装の考え方

実装では、実際にスクロールが発生している要素に対してのみ処理を行いました。

  • 対象は .jss_content(JSpreadsheet のスクロール領域)
  • wheel イベントを監視して横方向の操作を判定
  • 左方向 × 左端 のときだけ preventDefault()
  • passive: false を指定して確実に制御する
    passive: false を指定しないと、ブラウザ側の最適化により preventDefault() が無効になる場合があります。
const scrollable = productDataSheet.value?.querySelector('.jss_content') as HTMLElement | null

if (scrollable) {
  const onWheel = (e: WheelEvent) => {
    const isHorizontal = Math.abs(e.deltaX) > Math.abs(e.deltaY)
    const isScrollingLeft = e.deltaX < 0
    const atLeftEdge = scrollable.scrollLeft <= 0

    // 「横スクロールで左へ」かつ「もう左端」ならブラウザバック誤爆を防ぐ
    if (isHorizontal && isScrollingLeft && atLeftEdge) {
      e.preventDefault()
    }
  }

  scrollable.addEventListener('wheel', onWheel, { passive: false })

  // (必要なら)unmounted で removeEventListener
}

※コンポーネント破棄時には removeEventListener で後片付けしておくと、画面遷移が多い場合も安全です。

コード自体はシンプルですが、
「どこで」「どの操作だけを止めるか」を意識することが重要でした。


なぜこの方法を選んだか

この方法の良い点は、

  • 実装が比較的シンプル
  • 副作用が少ない
  • 表の操作感をほぼ変えずに済む

ことです。

「戻る操作を完全に禁止する」のではなく、
「業務上問題になる瞬間だけを防ぐ」ことを重視しました。


UX的に意識したこと

  • ユーザーに制御の存在を意識させない
  • 横スクロールの操作感は維持する
  • 表の外では通常のブラウザ挙動を保つ

業務アプリでは、
「操作できるが事故が起きる UI」よりも、「多少制限があっても安全な UI」
を優先する必要があると感じています。


まとめ

Jspreadsheet を業務アプリで使う場合、

  • 表操作が多い
  • 横スクロールが頻発する
  • 入力途中のデータが重要

といった条件が重なると、
ブラウザバック問題が顕在化しやすくなります

今回の対応では、

  • 問題が起きる範囲を限定し
  • UX を壊さず
  • 実装コストを抑える

ことを意識しました。

同じように Excel ライクな UI を扱っている方の参考になれば幸いです。


ほんだ

ほんだ

企画開発部エンジニア

コメント

この記事へのトラックバックはありません。

関連記事