Skip to content

ガイド

EffDNDは、属性だけでHTMLにドラッグ&ドロップ機能を追加します。JavaScript呼び出しは不要で、インポートするだけで設定も完了します。このガイドでは、EffDNDで何ができるのか、そしてどのように使うのかを解説します。

はじめに

sh
npm i effdnd

アプリに一度インポートしてください:

ts
import 'effdnd';            // ライブラリ
import 'effdnd/style.css';  // オプション: デフォルトスタイル

以上です。以下の属性でマークされた要素は、後からページに追加された要素も含め、すぐにドラッグ可能になります。

フレームワーク: どのフレームワークでも動作します。コンポーネントラッパーは不要です。

動作原理

EffDNDは一つのコアアイデアに基づいて構築されており、このガイドの他の部分はすべてその拡張です。

  • ドラッグを開始するのはtriggerのみです。 要素自体はドラッグできません。data-dnd-triggerでマークされたハンドルを掴むとドラッグが開始されます。トリガーがない場合、何も動きません。
  • 実際に動くのはitemです。 トリガーは通常、要素の内部に存在するため、要素全体がポインターに追従しますが、同じノードである必要はありません。
  • scopeはサンドボックスです。 itemは、そのscope内でのみ移動およびドロップできます。スコープ外のドロップゾーンやコンテナは無視されます。

これは基本ケースです。スコープ内のアイテムに対するトリガーです。

html
<div data-dnd-scope="board">              <!-- スコープ: サンドボックス -->
  <div data-dnd-item="a">                 <!-- アイテム: 何が動くか -->
    <span data-dnd-trigger>⠿</span>       <!-- トリガー: 掴んだもの -->
      私をつかんで
  </div>
</div>

以下に挙げるすべての機能(並べ替え、移動、スクロール、または下記のトリガーパラメータ)は、このコア動作を洗練または拡張するものです。

ドラッグ中のスタイリング 移動している要素は、item のクローン(コピー)であり、 アイテムの 自身の親 に挿入されます。つまり、カスケードされたスタイルや継承されたスタイル、 親セレクタ(ul > li.zone .card など)に対して記述されたルールは、移動するクローンにも適用されます。 すべての色をインライン化する必要はありません。元のアイテムはリスト内の所定の位置に留まり、 passive 状態によって暗くなります(下記の スタイリング を参照)。

注意点 - 変換された祖先要素 クローンは position:fixed であるため、 レイアウトから切り離されます。ただし、アイテムの親要素に transformfilterperspective (または will-change:transform) が設定されている場合、ブラウザは fixedabsolute のように扱います。クローンは その親要素に対して相対的に配置され、ずれて表示される可能性があります (また、スクロール可能な変換済みコンテナと共にスクロールします)。ドラッグしたアイテムの変換済み親要素を避けるか、 data-dnd-state とアイテムに適用するインラインスタイルを使用して、fixed-visual スタイルを上書きしてください。

属性の概要

属性機能
data-dnd-triggerユーザーがドラッグする「ハンドル」をマークします
data-dnd-itemドラッグされる要素をマークします
data-dnd-scopeドラッグとドロップのターゲットを1つのコンテナに制限します
data-dnd-reorderコンテナをソート可能なリスト(x または y)に変換します
data-dnd-targetドロップゾーンをマークします
data-dnd-transferドロップゾーンにアクション(appendprependremove)を設定します
data-dnd-scrollドラッグ中にコンテナを自動スクロールします
data-dnd-transition移動アニメーションを滑らかにします
data-dnd-disabledトリガーを無効にします
data-dnd-stateドラッグ中に設定されるランタイム状態(active/passive) - スタイル設定に使用されます

最も基本的なケースでは、コンテナ(scope)内の要素(item)に対するハンドル(trigger)を指定するだけで十分です。その他のパラメータはすべてオプションであり、動作を追加するものです。

トリガーパラメータ

「トリガー」はエントリ ポイントであるため、最も頻繁に調整する属性です。セミコロンで区切られたパラメータのリストを受け入れます。

html
<span data-dnd-trigger="dist:12;axis:y;scope:board;item:task-1">⠿</span>

ドラッグ距離(dist)を指定してください。

クリック時の意図しないドラッグを防ぎます。ポインターが指定されたピクセル数だけ移動した後でのみドラッグが実行されます。

html
<span data-dnd-trigger="dist:12">⠿</span>   <!-- ポインターが12px移動した後にのみドラッグします -->

1つの軸(axis)にロックする

html
<span data-dnd-trigger="axis:x">⠿</span>   <!-- 水平方向のみ -->
<span data-dnd-trigger="axis:y">⠿</span>   <!-- 垂直方向のみ -->

特定のコンテナ(scope)を指定する

デフォルトでは、EffDNDはDOMを調べて最も近いscopeを見つけます。特定のscopeが必要な場合は、名前を付けてください。

html
<span data-dnd-trigger="scope:board">⠿</span>

特定のアイテム(item)を指定する

トリガーは、その上にある特定のアイテムを移動対象として指定できます。

html
<span data-dnd-trigger="scope:board;item:task-1">⠿</span>

ドロップ先を制限する(target

data-dnd-targetが指定された名前で始まるゾーンへのドロップのみを許可する:

html
<span data-dnd-trigger="scope:board;target:drop">⠿</span>

ページ全体を自由に移動する(scope:*

特別な値scope:*は、scopeの境界を完全に無視します。

html
<span data-dnd-trigger="scope:*">⠿</span>

その後、アイテムはページ上のどこにでもドラッグできます。

トリガーを無効にする

html
<span data-dnd-trigger data-dnd-disabled>⠿</span>

再度有効にするには、属性を削除してください。

一般的なシナリオ

実際のインターフェースで各要素が組み合わされる一般的な例を以下に示します。

並べ替え:ソート可能なリスト

アイテムを data-dnd-reorder="y" でリストにラップし、各アイテムにマークを付け、ハンドルを追加します。

html
<ul data-dnd-scope="todo" data-dnd-reorder="y">
  <li data-dnd-item="1"><span data-dnd-trigger>⠿</span>Buy milk</li>
  <li data-dnd-item="2"><span data-dnd-trigger>⠿</span>Read a book</li>
</ul>
  • y は縦方向に、x は横方向にソートします。
  • リストの直接の子である項目のみが並べ替えられます。

転送:コンテナ間でアイテムを移動します。

コンテナをドロップゾーンとして指定し、転送アクションを設定します。

html
<div data-dnd-scope="sprint">
  <div class="zone" data-dnd-target="backlog" data-dnd-transfer="append">Backlog</div>
  <div class="zone" data-dnd-target="done" data-dnd-transfer="prepend">Done</div>
</div>
  • append — アイテムはゾーンの末尾に追加されます。
  • prepend — アイテムはゾーンの先頭に追加されます。
  • remove — アイテムは削除されます(ゴミ箱などに最適です)。

ドロップゾーンは同じscope内に存在する必要があります(またはscope:*を使用してください。上記を参照)。

組み合わせる:並べ替え可能なリストとゴミ箱

html
<div data-dnd-scope="mailbox">
  <ul data-dnd-reorder="y">
    <li data-dnd-item="1"><span data-dnd-trigger>⠿</span>Invoice</li>
    <li data-dnd-item="2"><span data-dnd-trigger>⠿</span>Newsletter</li>
  </ul>
  <button data-dnd-target="trash" data-dnd-transfer="remove">Delete</button>
</div>

ここではリストが自動的にソートされ、ボタンにドラッグ&ドロップすることで不要な行を削除できます。

カンバン:列とカード

ボードに列をソートするためのdata-dnd-reorder="x"を設定し、各列のカードエリアに垂直方向の並べ替えゾーンと転送レシーバーの両方を設定します。

html
<div data-dnd-scope="kanban" data-dnd-reorder="x">          <!-- 列をソートします -->
  <div class="col" data-dnd-item="col-1">
    <div class="col-head"><span data-dnd-trigger>⠿</span>Backlog</div>
    <div class="cards" data-dnd-reorder="y"
         data-dnd-target="col-1" data-dnd-transfer="append"> <!-- カードの仕分けと受け入れ -->
      <div class="card" data-dnd-item="task-1"><span data-dnd-trigger>⠿</span>Write spec</div>
    </div>
  </div>
  <!-- その他の列... -->
</div>

列ヘッダーをドラッグして列の順序を変更します。 カードをドラッグして列間を移動します。

一般的な動作のチューニング

長いコンテナを自動スクロールする

data-dnd-scroll をスクロール可能な要素にアタッチします。

html
<div data-dnd-scroll="threshold:50;speed:12">
  • threshold (デフォルト値 30) — スクロールを開始する端からの距離 (ピクセル単位)。
  • speed (デフォルト値 10) — スクロール速度 (1フレームあたりのピクセル数)。

端の近くをドラッグするとコンテナがスクロールし、端に近づくほどスクロール速度が速くなります。

アニメーションを滑らかにする

html
<div data-dnd-transition="150ms ease">…</div>

デフォルト 100ms linear.

JavaScriptからのドラッグ操作への反応

もちろん可能です。EffDNDでは、この機能をオプションとして利用できます。以下の各関数は、購読解除関数を返します。

ts
import { onDrag, onDrop, onReorder, onTransfer, onDragStart, onDragEnd } from 'effdnd';

onReorder((event) => {
  console.log('Item was reordered:', event.detail.keys.item);
});

利用可能なイベント: effdragstarteffdrageffdragendeffdragentereffdragleaveeffdropeffreorderefftransfer

各イベントは、信頼できる detail オブジェクトを提供します。

ts
event.detail.keys.item;          // どのアイテム
event.detail.keys.scope;         // どのスコープコンテナ内
event.detail.keys.target;        // どのターゲットに対して
event.detail.item;

役立つかもしれないヘルパー

ts
import { getItem, getScope, getTargets, getReorderContainer, reset } from 'effdnd';

getItem(trigger);           // 要素が属する `item`
getScope(trigger);          // 要素が属するスコープ
getTargets(trigger);        // 利用可能な`target`要素
reset(item);                // アイテムを元の位置に戻す

data-dnd-state によるスタイリング

ドラッグ中、EffDND は関連する要素にランタイム属性 data-dnd-state を付与します。これにより、アクティブなドラッグをスタイリングするための簡潔なフックが提供され、インライン JavaScript は不要になります。何もドラッグされていないときは、data-dnd-state は存在しません。

状態値

要素状態意味
data-dnd-itemactive移動中のクローン — ポインターに追従している要素
data-dnd-itempassive元のアイテムはその場に残され、クローンの背後で暗く表示されます
data-dnd-scopeactiveポインターがスコープの境界外にある
data-dnd-scopepassiveポインターがスコープ内にある — ドラッグ中の通常状態
data-dnd-targetactive現在ホバーしているドロップゾーン
data-dnd-targetpassiveドロップを受け入れる準備ができている有効なドロップゾーン

同梱されているindex.cssには、これらのセレクタに基づいて構築された洗練されたデフォルト設定が既に含まれており、それらを上書きするには同じセレクタを使用します。

css
/* index.css からデフォルト値を設定 */
[data-dnd-item][data-dnd-state="passive"] { opacity: 0.2; }
[data-dnd-item][data-dnd-state="active"]  { opacity: 0.75; z-index: 1000; }

/* 独自のテーマ */
[data-dnd-item][data-dnd-state="passive"] { opacity: 0.08; }
[data-dnd-item][data-dnd-state="active"]  {
    opacity: 1;
    box-shadow: 0 0 0 2px cornflowerblue;
    border-radius: 8px;
}
[data-dnd-scope][data-dnd-state="active"] { outline: 2px dashed tomato; }
[data-dnd-target][data-dnd-state="active"] { background: rgba(100, 200, 255, 0.2); }

デフォルト設定は index.css にあるため、それらを読み込むのは オプション です。インポートをスキップして独自の [data-dnd-state="…"] ルールを作成すれば、EffDND は完全に依存関係のない状態を維持し、視覚的な制御を完全に行うことができます。

ヒント 元の要素は削除されるのではなく、opacity によって暗く表示されます。そのため、リストのレイアウトは維持され、

ドラッグ中に兄弟要素がジャンプすることはありません。元の要素を完全に非表示にしたい場合は、 passive 状態に opacity: 0 (または visibility: hidden) を設定してください。

ミニリファレンス

すべての属性とそのパラメータを簡潔にまとめたものです。

属性 / パラメータデフォルト値目的
data-dnd-triggerドラッグを開始するハンドルをマークします
… dist数値ポインタが指定されたピクセル数だけ移動した後にのみドラッグします
… axisx / ybothドラッグを1つの軸に固定します
… scope名前 / *nearest in DOM特定のコンテナを使用します(またはスコープを無視します)
… item名前nearest in DOM特定のアイテムをドラッグします
… target名前all名前に一致するターゲットへのドロップのみを許可します
data-dnd-disabledトリガーを無効にします
data-dnd-item一意の名前ドラッグされる要素をマークします
data-dnd-scope一意の名前ドラッグ/ドロップを1つのコンテナに制限します
data-dnd-reorderx / yコンテナをソート可能なリストに変換します
data-dnd-target名前ドロップゾーンを指定します
data-dnd-transferappend / prepend / removeゾーンにドロップされたときに実行されるアクション
data-dnd-scrollthreshold;speed30;10ドラッグ中にコンテナを自動スクロールします
data-dnd-transitionduration & easing100ms linear移動アニメーションを滑らかにします
data-dnd-stateactive / passive実行時のドラッグ状態、CSS でスタイル設定(スタイリング を参照)

Apache License 2.0に基づいて公開されています。