HTMLのdraggable属性でユーザが要素を自由に配置できるようにする
時間掛かると思ったらあっさり出来たので、簡単にまとめる
概要
各要素(panel)をドラッグ・要素の間の隙間にドロップすると、その位置に要素を移動させます。
解説
draggable
HTML Drag and Drop API を使います。主に draggable
属性、drag関係のイベント、event.dataTransfer
から取得出来る DataTransfer オブジェクトから構成されます。
要素をドラッグ可能にするには、element に draggable='true'
を指定するだけです。簡単。
要素をドロップゾーンとするには、drag
イベントハンドラの実装の他に、dragover
を event.preventDefault();
する必要があります。
ところで、HTML Drag and Drop API は主に文字列や画像の転送を主としています。HTML の element から element へドロップした場合でも、ドラッグ元の要素を API から取得することはできません。実装方針は以下が考えられますが、サンプルでは1つ目を実装しています。
- dragstart イベントで、要素をグローバル変数にセットしておき、drop イベントでそのグローバル変数を参照する
- pros: Element 等の全ての型が使えるので変換が不要
- cons: グローバル変数・シングルトンを使う
- cons: dragend が呼ばれないケースを考慮出来ない。
- dragstart イベントで、要素を特定するための情報を
event.dataTransfer.setData
する- pros: グローバル変数を必要としない
- cons: テキストから Element の情報を復元しなければならない
- cons: ブラウザ外のテキストエディタにドロップ出来てしまう
レイアウト
疑似 HTML コードで表現すると、初期状態は以下のようになっています(実際はdiv等です)
<app>
<row>
<panel>...</panel>
<panel>...</panel>
</row>
<row>
<panel>...</panel>
</row>
</app>
javascript で以下のように、row の前後と panel の前後に padding を動的に追加しています。ユーザは、panel を padding にドラッグ・ドロップします。
<app>
<padding></padding>
<row>
<padding></padding>
<panel>...</panel>
<padding></padding>
<panel>...</panel>
<padding></padding>
</row>
<padding></padding>
<row>
<padding></padding>
<panel>...</panel>
<padding></padding>
</row>
<padding></padding>
</app>
Node.insertBefore
で panel を動かしたら、paddingやrowも再構成する必要があるのですが、面倒だったので雑にdocument.querySelectorAll(".padding").forEach((e) => e.remove());
して、もう一度初期化の実装を走らせています。パフォーマンスに難があるけど、わかりやすいしお試し実装だしいいや
Discussion