dnd-kit を使ってドラッグ&ドロップを実装する際、フォーム要素(input や button など)を含む場合に注意が必要だった
ドラッグ操作とクリック・入力操作が競合しないように制御する方法を調べたのでメモ
発生した問題
inputやselectをクリックすると、意図せずドラッグが開始される- ボタンを押したいだけなのに、ドラッグ挙動が優先される
- スマートフォン環境でタップ操作とドラッグ操作が干渉する
解決方法1: イベント伝播を止める
フォーム要素に対して、onPointerDown などのイベントでドラッグ操作への伝播を防ぐ
実装例
<input type="text" onPointerDown={(e) => e.stopPropagation()} /> <button onPointerDown={(e) => e.stopPropagation()}> ボタン </button>
ポイント
- ドラッグを開始させたくない要素に限定して適用する
- マウス操作・タッチ操作の両方に対応するには onPointerDown を使うと良い
ただ、あまりに多いと全部に適用するのはつらい
方法2: activationConstraint を使う
DndContextに設定するsensor の設定の1つ activationConstraint では、ドラッグ開始条件を細かく制御できる
一定距離動かした場合のみドラッグを開始するなどの設定が可能
実装例
const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5, }, }) ); ... <DndContext sensors={sensors}>
よく使うパターン
| 設定項目 | 内容 |
|---|---|
| distance | 指定距離以上動かした場合のみドラッグ開始 |
| delay | 長押しした場合のみドラッグ開始(ms単位) |
| tolerance | タッチ操作時の誤差許容範囲 |
方法3: Drag Handle を導入する
ドラッグ操作を特定の領域やアイコンに限定することで、フォーム操作との干渉を避ける
listeners を全体に付けるのではなく、ドラッグ専用のハンドル部分にだけ適用する
実装例
<div ref={setNodeRef} {...attributes}> <span>ドラッグ対象の内容</span> <span {...listeners} className="drag-handle">≡</span> </div>