見出し画像

[備忘録] Webアプリdnd処理まとめ

とくにPC利用者のユーザビリティーがバク上がりするドラッグ&ドロップ処理はWebアプリでも画像のドロップやリストの並べ替えなどでよく使われます。仕様として書くと1行ですが実際は色々あるので書いておくことにしました。

draggable 設定

<ul>
   <li class="droppable" :id="index" draggable="true" >
   ...
</ul>

イベント設定

dragstart, drop の2つは最低必要。

dragestart でドラッグするアイテムの情報取得
drop でドロップした相手とドラッグしているアイテムの情報を渡す

	window.addEventListener("dragstart", { handleEvent: this.onDragStart });
	window.addEventListener("drop", { handleEvent: this.onDrop });

イベントは登録だけでなく削除のコードも忘れずに。

dataTransfer

dndイベントのメンバ  dataTransfer  でドラッグしたアイテムの情報をドロップ先に渡す仕組みとなっている。 dataTransferはどこでもアクセスできるわけではなく、 dragstart , drop イベントだけ。

// in dragstart event
event.stataTransfer.setData( 'text' , e.target.id )

...

// in drop event
id = event.stataTransfer.getData( 'text'  )

ドロップ先のオブジェクトの確定

drop先のオブジェクトが単要素でない場合(親子で要素がある)場合、dropイベントが呼ばれてから要素を適宜特定する必要がある。
drop イベントで event.target に設定されるのはドロップしたエリアの親要素ではなく、子要素であるため。

<li class=droppable" id="3" >
	<div>
		<p> ... </p>
		<img />
		<p> ... <span> ...</span> </p>
		..
</li>

dropイベントでは class="droppable の li の id=3 を取得してドロップ先を特定したいが、event.target に入るのは span である可能性もある。
このため、

for (let ii = 0; ii < SearchMax ; ii++) {
	if (node.classList.contains('droppable')) {
		console.log('Dropped on: ' + node.id);
		return node.id
	}
    node = node.parentNode;
}

などのように要素を遡って想定する要素の情報を取得する必要がある。

Fileの場合

ファイルのdndの場合、多くのケースで

  • ファイルかどうか

  • 目的の拡張子のファイルか

2つの判定をする必要が出てきます。できるだけ早く検出しフィードバッグするのがユーザビリティーは高くなるが制約があります。

ファイルのDnDを期待している場合、dragstart やdragenter イベントで ドラッグされているのがファイルか確認することができる。
Fileの情報そのものにはdropイベントでないとアクセスできないため、拡張子の確定はできないが、ファイルかどうかはここで判定が可能。

	if (!e.dataTransfer.types.includes('Files')) {

	}


複数種類のdnd対応UIがある場合

同一画面に複数のdnd要素がある場合、すべてのdnd アイテムが互のUIグループにdndできれば単純に作れますが、画像のドロップ、名簿リストの並べ替えなど別々に扱う要素のdndがある場合は実装で注意が必要です。
とくにReact や Vueを使用してdndする機能ごとComponentになっている場合、なにかドラッグするとすべてのdndコンポーネントで設置したイベントが呼ばれ、処理が衝突する可能性があります。

  • 実装前に、まずはdndが単一か、複数ある場合は同一ITEMが行き来するか仕様を確認。

  • 作業見積もりを単一の場合とは別に、テストも同様

  • dragstart, dropでstateTransfer をjson化するなどして、別UIと区別できる処理を入れる。

  • ファイルドロップ併用の場合、ファイルと関係ないdndがあるUIでは FILEを除外する処理

  • テストにUI同士のdndを必ず入れる

などが必要になります。経験が浅いメンバーだけでなく顧客含め、PMや作業者が見積もりで意識しているか、上記関連の確認をする。

バグるとややこしいことに

イベントの削除

これもcomponent内に実装するときには特に注意ですが、componentの削除時にeventがゾンビ化しないように削除するコードを忘れないように追加します。イベント削除はgrepなどを利用してコードベースのチェックがおすすめ。

リストのdnd/データの管理方法と描画の都合

これも複数のdnd リストがあるような場合の検討事項ですが、UI Compnent毎に処理のポリシーが違うと後々ソースを見るときの面倒さが違ってきますので、複数ある場合はデータの持ち方と描画について検討しルールを決めるのがおすすめ。

リストの並べ替えでは、リスト表示を作るときにjs/framework でindexを使った配列処理を書くと簡単ですが、後々バグ要因になるケースがあるため、IDによる解決に統一するか、関数APIの設計で検討の余地あり。
並び順はID配列にしておき、実体はIDによるオブジェクトDICTにするような作りだと並び替え処理諸々が複雑に絡んでもバグになりにくいケースがあります。

APIからもらったまま描画したいのは人情ですが、多少無駄に感じてもデータ管理はワンクッションいれるとパフォーマンスや安定性が上がる場合があります


この記事が気に入ったらサポートをしてみませんか?