🎛️

HTMLのdraggable属性でユーザが要素を自由に配置できるようにする

2022/05/07に公開

時間掛かると思ったらあっさり出来たので、簡単にまとめる

概要

各要素(panel)をドラッグ・要素の間の隙間にドロップすると、その位置に要素を移動させます。

解説

draggable

https://developer.mozilla.org/ja/docs/Web/API/HTML_Drag_and_Drop_API

HTML Drag and Drop API を使います。主に draggable 属性、drag関係のイベント、event.dataTransfer から取得出来る DataTransfer オブジェクトから構成されます。

要素をドラッグ可能にするには、element に draggable='true' を指定するだけです。簡単。
要素をドロップゾーンとするには、drag イベントハンドラの実装の他に、dragoverevent.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