ドラッグアンドドロップは、優れたインターフェイスソリューションです。何かを取り、ドラッグアンドドロップすることは、ドキュメントのコピーと移動(ファイルマネージャーの場合のように)から注文(アイテムをカートにドロップする)まで、多くのことを行うための明確で簡単な方法です。
最新のHTML標準では、dragstart
、dragend
などの特別なイベントを含むドラッグアンドドロップに関するセクションがあります。
これらのイベントにより、OSファイルマネージャーからのファイルのドラッグアンドドロップの処理など、特別な種類のドラッグアンドドロップをサポートできます。その後、JavaScriptはそのようなファイルのコンテンツにアクセスできます。
ただし、ネイティブのドラッグイベントにも制限があります。たとえば、特定の領域からのドラッグを防ぐことはできません。また、ドラッグを「水平」または「垂直」のみにすることはできません。そして、それらを使用して実行できない他の多くのドラッグアンドドロップタスクがあります。また、このようなイベントに対するモバイルデバイスのサポートは非常に弱いです。
ここでは、マウスイベントを使用してドラッグアンドドロップを実装する方法を説明します。
ドラッグアンドドロップアルゴリズム
基本的なドラッグアンドドロップアルゴリズムは次のようになります。
-
mousedown
の場合–要素を移動する準備をします。必要です(おそらく、クローンを作成したり、クラスを追加したりします)。 - 次に、
mousemove
で、とposition:absolute
。 -
mouseup
の場合–ドラッグアンドドロップの終了に関連するすべてのアクションを実行します。 ‘ドロップ。
これらが基本です。後で、ドラッグ中に現在の基になる要素を強調表示するなど、他の機能の方法を説明します。
ボールのドラッグの実装は次のとおりです。
コードを実行すると、何か奇妙なことに気付くことができます。ドラッグアンドドロップの開始時に、ボールは「フォーク」します。「クローン」のドラッグを開始します。
実際の例を次に示します。
マウスでドラッグアンドドロップを試みると、そのような動作が見られます。
これは、ブラウザが独自のドラッグアンドドロップで画像をサポートしているためです。他のいくつかの要素。自動的に実行され、競合します。
無効にするには:
これですべてが正常になります。
実行中:
もう1つの重要な側面– ball
ではなく、document
でmousemove
を追跡します。一見すると、マウスは常にボールの上にあるように見えるかもしれません。その上にmousemove
を置くことができます。
しかし、覚えているように、mousemove
は頻繁にトリガーされますが、すべてのピクセルに対してトリガーされるわけではありません。したがって、すばやく移動すると、ポインタがボールからドキュメントの中央のどこか(またはウィンドウの外側)にジャンプする可能性があります。
したがって、document
をリッスンする必要があります。
正しい位置
上記の例では、ボールは常に移動し、中心がポインターの下にくるようにします。
悪くはありませんが、副作用があります。ドラッグアンドドロップを開始するには、ボールのどこにでもmousedown
できます。しかし、その端から「取る」と、ボールは突然「ジャンプ」して、マウスポインタの下の中央に配置されます。
ポインタに対する要素の最初のシフトを維持する方がよいでしょう。
たとえば、ボールの端でドラッグを開始した場合、ドラッグ中はポインタが端の上に留まる必要があります。
アルゴリズムを更新しましょう:
より適切な位置に配置された最終コード:
動作中(<iframe>
内) :
ボールを右下隅でドラッグすると、違いが特に顕著になります。前の例では、ボールはポインターの下で「ジャンプ」します。これで、ボールは現在の位置からポインターに流暢に追従します。
潜在的なドロップターゲット(ドロップ可能)
前の例では、ボールはとどまるためにちょうど「どこでも」落とされます。実生活では、通常、ある要素を別の要素にドロップします。たとえば、「ファイル」を「フォルダ」などに挿入します。
要約すると、「ドラッグ可能」要素を取得して「ドロップ可能」要素にドロップします。
知る必要がある:
- ドラッグアンドドロップの最後に要素がドロップされた場所–対応するアクションを実行するために
- そして、できれば、ドロップ可能なドラッグしてハイライト表示します。
このソリューションは興味深いもので、少し注意が必要なので、ここで説明します。
最初のアイデア?おそらく、潜在的なドロップ可能オブジェクトにmouseover/mouseup
ハンドラーを設定しますか?
しかし、それは機能しません。
問題は、私たちがいる間、それです。ドラッグすると、ドラッグ可能な要素は常に他の要素の上にあります。また、マウスイベントは上部の要素でのみ発生し、その下の要素では発生しません。
たとえば、以下は2つの<div>
要素で、赤が青の上にあります。 1つ(完全にカバー)。赤が上にあるため、青のイベントをキャッチする方法はありません。
ドラッグ可能な要素でも同じです。ボールは常に他の要素の上にあるため、イベントが発生します。下位の要素にどのハンドラーを設定しても、それらは機能しません。
そのため、潜在的なドロップ可能オブジェクトにハンドラーを配置するという最初のアイデアは実際には機能しません。実行されません。
では、どうすればよいですか?
document.elementFromPoint(clientX, clientY)
というメソッドがあります。指定されたウィンドウ相対座標(または指定された座標がウィンドウ外にある場合はnull
)で最もネストされた要素を返します。
任意の場所で使用できます。次のように、ポインタの下にドロップできる可能性のあるものを検出するためのマウスイベントハンドラ:
注意:(*)
を呼び出す前にボールを非表示にする必要があります。それ以外の場合は、通常、これらの座標にボールがあります。これは、ポインターの下の一番上の要素であるelemBelow=ball
です。そのため、非表示にしてすぐに再度表示します。
そのコードを使用して、いつでも「飛んでいる」要素を確認できます。ドロップが発生した場合は処理します。
「ドロップ可能な」要素を見つけるためのonMouseMove
の拡張コード:
以下の例では、ボールがサッカーゴールの上にドラッグされると、ゴールが強調表示されます。
これで、プロセス全体で変数currentDroppable
に現在の「ドロップターゲット」があり、それを使用してハイライトまたはその他のもの。
概要
基本的なドラッグアンドドロップアルゴリズムを検討しました。
主要コンポーネント:
- イベントフロー:
ball.mousedown
→document.mousemove
→ball.mouseup
(ネイティブをキャンセルすることを忘れないでくださいondragstart
)。 - ドラッグの開始時–要素に対するポインタの最初のシフトを覚えておいてください:
shiftX/shiftY
ドラッグ中はそのままにしておきます。 - ドロップ可能な要素を検出し、
document.elementFromPoint
を使用してポインタを作成します。
この基盤に多くのことを置くことができます。
-
ドロップを知的に完成させることができます:データの変更、要素の移動。
- 飛んでいる要素を強調表示できます。
- 私たちは特定の領域または方向によるドラッグを制限できます。
-
mousedown/up
のイベント委任を使用できます。event.target
をチェックする大面積のイベントハンドラーは、何百もの要素のドラッグアンドドロップを管理できます。 - など。
その上にアーキテクチャを構築するフレームワークがあります:DragZone
、Droppable
、Draggable
および他のクラス。それらのほとんどは上記と同様のことを行うので、今では簡単に理解できるはずです。または、自分で作成することもできます。これは、サードパーティのソリューションを採用するよりも簡単な場合もあります。