Kitsune Gadget

気になったことをつらつらと

ドロップ要素に子要素を含む場合のホバーエフェクト

子要素を持たない要素の範囲だけにドロップさせるような機能であれば、その要素に設定した ID やクラス属性で条件設定すると思います。そのうち、扱いやすい UI にするために子要素を持つ要素全体をドロップ範囲にしたい場合もあるでしょう。その場合のドロップ出来るポイントを伝えるために、カーソル表記(ドロップエフェクト)を変更することもひとつですが、要素そのものに効果があるとよりわかりやすくなります。要素を変更させる場合は範囲内のどこにマウスがあっても全体が変わるような効果が欲しいはず。dragenter, dragleave を利用するのが基本的ですが、残念なことに要素の親子間でも発火するため、処理が複雑になりがちです。

問題点

  • 要素全体のスタイルを変更する場合、変更するすべての子要素に静的 or 動的に同名クラスを追加しなければならない。そもそもボタンやキャンバスが含まれていて背景を変える部分が無いなどでスタイルを変更しにくい。

  • dragenter, dragleave は要素の子要素に入る時も発火するため、スタイルの変更がチラつく。

改善1

absolute 要素でドロップ範囲を覆うことで、新しいホバー要素として追加する。さらにドロップ処理も absolute 側に書く。

親要素の dragenter でホバー要素表示。

absolute 要素から外に出るときに absolute 要素を戻す処理ができない。

dragleave を absolute 要素側で処理。

absolute 要素内の子要素に入った時に leave してしまう。

親要素の dragenter を dragover にする。

ここまでやれば、ほぼ問題なく動作する。しかし、absolute 要素の子要素に入ったときの微妙なチラつきも直したい場合は以下にようにした。

改善2

body 要素全体を dragover にする。

Node.contains を利用して、ドロップ範囲内に入ったら absolute 要素を表示。ドロップ処理は改善1と同様に absolute 側。

ドラッグ中は常に dragover が発火しているので要素の外ではスタイルを戻す処理を追加すれば良い。

body要素全体を dragover にすることで、ドロップ範囲の外でも処理を実行できるため、ホバーエフェクトは綺麗に表示できる。さらに、複数のドロップ先がある場合も Node.contains を利用して条件分けも可能。body内はすべてドラッグ範囲になるため、ドロップ範囲外にはドロップできない旨を伝えるためにドロップエフェクトに none を書いておきましょう。

デモ

https://jsfiddle.net/y46ga21u/1/