内部構造:ドラッグ、タッチ、そしてCSSのカスケード

リサイズ可能なUIパネルの構築は、見た目以上に困難です。レイアウト、タッチイベント、そしてCSSルールを同時に管理しなければなりません。

最近行ったビジュアル差分(visual-diff)UIの再設計から得られた、3つの技術的な教訓を紹介します。

1. レイアウトにウィンドウ幅を使用するのをやめる

レイアウトが垂直か水平かを判断するために window.innerWidth を使用しないでください。ユーザーがウィンドウのサイズを変更すると、ロジックが壊れてしまいます。

代わりに、実際のCSSの状態を使用してください。getComputedStyle を使って flex-direction を確認します。これにより、すべてのメディアクエリが適用された後の実際の値を読み取ることができます。これにより、ドラッグのロジックがユーザーの実際の表示と一致することが保証されます。

2. タッチイベントとマウスイベントをまとめて扱う

デスクトップとモバイルの両方でドラッグを機能させるには、統一されたアプローチが必要です。

  • オプショナルチェイニングを使用して座標を抽出します。これにより、1つの関数でマウスイベントとタッチイベントの両方を処理できるようになります。
  • touchmove には { passive: false } を使用してください。ブラウザはスクロールをスムーズにするために、タッチイベントのデフォルトを passive に設定しています。これを false に設定しないと、e.preventDefault() を呼び出すことができません。その呼び出しがないと、ドラッグしようとしている間にページがスクロールしてしまいます。
  • moveend のリスナーを document にアタッチします。ユーザーが指を素早く動かしすぎると、仕切り(divider)から外れてしまうことがあります。document にアタッチすることで、ポインターが逸れてもドラッグが継続されるようになります。

3. 表示制御システムとの衝突を避ける

CSSの詳細度(specificity)に関するバグに遭遇しました。パネルを非表示にするためのクラスと、レイアウトの方向を設定するためのクラスが別々に存在していました。

問題点:

  • .view-panel { display: none; }
  • .diff-layout { display: flex; }

これらは詳細度が同じであったため、ファイル内で後に記述された方が優先されました。その結果、パネルを非表示にすべき時でも表示されたままになってしまいました。

解決策: レイアウト用のヘルパークラスに display プロパティを設定させないでください。それは表示制御用のクラスの役割に留めます。

修正前: .diff-layout { display: flex; flex-direction: column; }

修正後: .view-panel.diff-layout { flex-direction: column; }

これにより詳細度が上がり、レイアウトクラスが「表示状態」ではなく「方向」のみを変更することを保証できます。

出典: https://dev.to/bonzai2carn/under-the-hood-drag-touch-and-css-cascade-in-a-real-diff-ui-1b66