はじめに
業務アプリケーションで、Excel ライクな操作感を求められる場面は少なくありません。
今回のプロジェクトでも、表形式での編集やコピー&ペーストといった要件から Jspreadsheet を利用していました。
実装自体は問題なく進んでいたのですが、実運用に入ってから
想定していなかった UX 上の問題が発生しました。
それが、横スクロール操作によってブラウザバックが発生してしまう問題です。
本記事では、
- 実際に起きた問題
- 原因の切り分け
- 試したが解決しなかった方法
- 最終的に採用した対策
を順に紹介します。
起きた問題:横スクロールで画面が戻る
Jspreadsheet では、列数が多い場合に横スクロール操作が発生します。
PC ではトラックパッド、Mac では 2 本指スワイプで操作することが多いでしょう。
しかし、ある画面で次のような問題が起きました。
- 表を横にスクロールしようとする
- 意図せずブラウザバックが発生する
- 入力途中のデータが消える
特に 業務中の連続操作で頻発し、
ユーザー体験としては致命的な問題でした。
なぜ起きたのか
原因は、ブラウザの履歴ナビゲーションと横スワイプ操作の競合でした。
- ブラウザ(特に macOS + Chrome / Safari)
- トラックパッドの横スワイプ
- 画面全体で有効な履歴操作
この状態で、Jspreadsheet の横スクロール操作が
ブラウザに「戻るジェスチャー」として解釈されてしまうと、
意図せず画面遷移が発生します。
Jspreadsheet 自体の不具合というより、
ブラウザ挙動と UI 構成の組み合わせによって起きた問題でした。
最初に試したがダメだった方法
① Jspreadsheet 側の設定で制御する
Jspreadsheet のオプションで解決できないか調べましたが、
- スクロール挙動自体を制御する設定はない
- ブラウザの履歴操作までは制御できない
という結論に至りました。
② CSS で overflow を調整する
overflow-x: auto;
といった対応も試しましたが、
- 表の外側でスワイプされると意味がない
- 根本的な解決にはならない
という問題が残りました。
③ 画面全体でイベントを止める
wheel や touch イベントを画面全体で 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 を扱っている方の参考になれば幸いです。
コメント