web-dev-qa-db-ja.com

dragenterおよびdragoverイベントから何がドラッグされているかを判断する

HTML5のドラッグ可能なAPIを使用しようとしています(ただし、 問題がある )。これまでのところ、私が遭遇した唯一のショートップは、dragoverまたはdragenterイベントが発生したときに何がドラッグされているかを判断する方法を見つけられないことです。

el.addEventListener('dragenter', function(e) {
  // what is the draggable element?
});

dragstartイベントを発生させる最後の要素であると想定できますが、...マルチタッチです。 dragstartからe.dataTransfer.setDataを使用して一意の識別子を付加しようとしましたが、明らかにデータは アクセス不能 from dragover/dragenter

このデータは、ドロップイベント中にドロップが発生した場合にのみ使用可能になります。

だから、アイデアはありますか?

更新:この記事の執筆時点では、HTML5のドラッグアンドドロップは主要なモバイルブラウザに実装されていないようで、練習。ただし、複数の要素が同時にドラッグされることを妨げるようには見えない 仕様 の実装全体で機能することが保証されているソリューションが必要です。

以下に 有効なソリューション を投稿しましたが、これはいハックです。私はまだより良い答えを望んでいます。

63
Trevor Burnham

私の質問に対する短い答えは次のとおりです。いいえ。 WHATWG spec は、dragenterdragover、またはdragleaveイベント。

何故なの? 2つの理由:

まず、Jefferyが 彼のコメント で指摘しているように、WHATWG仕様はIE5 +のドラッグアンドドロップの実装に基づいており、マルチタッチデバイスよりも前のものです。 (この記事の執筆時点では、主要なマルチタッチブラウザはHTMLドラッグアンドドロップを実装していません。)「シングルタッチ」コンテキストでは、dragstartに現在のドラッグされた要素へのグローバル参照を簡単に保存できます。

次に、HTMLのドラッグアンドドロップにより、複数のドキュメントに要素をドラッグできます。これは素晴らしいですが、すべてのdragenterdragover、またはdragleaveイベントでドラッグされている要素への参照を提供しても意味がないことを意味します。別のドキュメントの要素を参照することはできません。 APIの強みは、ドラッグが同じドキュメントで作成された場合でも、異なるドキュメントで作成された場合でも、これらのイベントが同じように機能することです。

しかし、dataTransfer.types(私の 作業ソリューション 回答で説明されている)を除いて、すべてのドラッグイベントにシリアル化された情報を提供できないことは、APIの明白な省略です。 私は ドラッグイベントで公開データの提案を提出した WHATWGに、あなたのサポートを表明することを願っています。

21
Trevor Burnham

ここを非常に明確な答えを付け加えたかったので、ここを通り過ぎるすべての人に明白になりました。それは他の答えで何度か言われていますが、ここで私ができる限り明確です:

dragoverには、ドラッグイベントのデータを表示する権利がありません。

この情報は、DRAG_STARTおよびDRAG_END(ドロップ)の間のみ利用可能です。

問題は、それがまったく明らかではなく、仕様やここのような場所で十分に深く読めるまで気が狂うことです。

回避策:

可能な回避策として、DataTransferオブジェクトに特別なキーを追加し、テストしました。たとえば、効率を向上させるために、「ドラッグオーバー」が発生するたびにではなく、ドラッグの開始時に「ドロップターゲット」ルールを検索したかったのです。これを行うために、各ルールを識別するキーをdataTransferオブジェクトに追加し、「含む」でテストしました。

ev.originalEvent.dataTransfer.types.includes("allow_drop_in_non_folders")

そして、そのようなもの。明確に言うと、「含む」というのは魔法の弾丸ではなく、それ自体がパフォーマンスの問題になる可能性があります。使用法とシナリオをよく理解してください。

53
bladnman

(非常に洗練されていない)解決策は、セレクターをtypeのデータとしてdataTransferオブジェクトに格納することです。以下に例を示します: http://jsfiddle.net/TrevorBurnham/eKHap/

ここでアクティブな行は

e.dataTransfer.setData('text/html', 'foo');
e.dataTransfer.setData('draggable', '');

次に、dragoverおよびdragenterイベントで、e.dataTransfer.typesには文字列'draggable'が含まれます。これは、どの要素がドラッグされているかを判断するために必要なIDです。 (ブラウザは、これが機能するためにtext/htmlのような認識されたMIMEタイプにもデータを設定する必要があるようです。ChromeおよびFirefox)でテストしました。)

それはい、いハックであり、誰かが私にもっと良い解決策を与えることができれば、私は喜んで彼らに報奨金を与えます。

更新:追加する価値がある1つの注意点は、エレガントではないことに加えて、仕様ではすべてのデータ型が小文字のASCIIに変換されることを示しています。そのため、大文字またはユニコードを含むセレクターが破損することに注意してください。 Jefferyのソリューション はこの問題を回避します。

6
Trevor Burnham

現在の仕様を考えると、「ハック」ではない解決策はないと思います。 Petitioning WHATWGはこれを修正する1つの方法です:-)

「(非常に洗練されていない)ソリューション」( demo )の展開:

  • 現在ドラッグされているすべての要素のグローバルハッシュを作成します。

    var dragging = {};
    
  • dragstartハンドラーで、ドラッグIDを要素に割り当て(まだ持っていない場合)、要素をグローバルハッシュに追加し、ドラッグIDをデータ型として追加します。

    var dragId = this.dragId;
    
    if (!dragId) {
        dragId = this.dragId = (Math.random() + '').replace(/\D/g, '');
    }
    
    dragging[dragId] = this;
    
    e.dataTransfer.setData('text/html', dragId);
    e.dataTransfer.setData('dnd/' + dragId, dragId);
    
  • dragenterハンドラーで、データ型の中からドラッグIDを見つけ、グローバルハッシュから元の要素を取得します。

    var types = e.dataTransfer.types, l = types.length, i = 0, match, el;
    
    for ( ; i < l; i++) {
        match = /^dnd\/(\w+)$/.exec(types[i].toLowerCase());
    
        if (match) {
            el = dragging[match[1]];
    
            // do something with el
        }
    }
    

draggingハッシュを自分のコードに対してプライベートにすると、サードパーティのコードはドラッグIDにアクセスできても、元の要素を見つけることができません。

これは、各要素を一度だけドラッグできることを前提としています。マルチタッチでは、異なる指を使用して同じ要素を複数回ドラッグすることができると思います...


更新:同じ要素に複数のドラッグを許可するには、グローバルハッシュにドラッグカウントを含めることができます: http:// jsfiddle .net/jefferyto/eKHap/2 /

4
Jeffery To

ドラッグの開始時に何がドラッグされているかを判断し、これを変数に保存して、dragover/dragenterイベントが発生したときに使用できます。

var draggedElement = null;

function drag(event) {
    draggedElement = event.srcElement || event.target;
};

function dragEnter(event) {
    // use the dragged element here...
};
3
tjscience

dragイベントで、event.xおよびevent.yをオブジェクトにコピーし、ドラッグされた要素のexpandoプロパティの値として設定します。

function drag(e) {
    this.draggingAt = { x: e.x, y: e.y };
}

dragenterおよびdragleaveイベントで、expandoプロパティ値が現在のイベントのevent.xおよびevent.yと一致する要素を見つけます。

function dragEnter(e) {
    var draggedElement = dragging.filter(function(el) {
        return el.draggingAt.x == e.x && el.draggingAt.y == e.y;
    })[0];
}

調べる必要のある要素の数を減らすために、要素を配列に追加するか、dragstartイベントでクラスを割り当て、dragendイベントで元に戻すことで要素を追跡できます。 。

var dragging = [];
function dragStart(e) {
    e.dataTransfer.setData('text/html', '');
    dragging.Push(this);
}
function dragEnd(e) {
    dragging.splice(dragging.indexOf(this), 1);
}

http://jsfiddle.net/gilly3/4bVhL/

今、理論的にはこれでうまくいくはずです。ただし、タッチデバイスのドラッグを有効にする方法がわからないので、テストできませんでした。このリンクはモバイル用にフォーマットされていますが、タッチアンドスライドによってAndroidでドラッグが開始されませんでした。 http://fiddle.jshell.net/gilly3/4bVhL/1/show/

Edit:私が読んだことから、HTML5ドラッグ可能はどのタッチデバイスでもサポートされているようには見えません。タッチデバイスでドラッグ可能な作業をすることができますか?そうでない場合、マルチタッチは問題にならず、ドラッグした要素を変数に保存するだけで済みます。

3
gilly3

ファイルかどうかを確認するには:

e.originalEvent.dataTransfer.items[0].kind

タイプの使用を確認するには:

e.originalEvent.dataTransfer.items[0].type

つまり、jpg、png、gif、bmpの1つのファイルのみを許可したい

var items = e.originalEvent.dataTransfer.items;
var allowedTypes = ["image/jpg", "image/png", "image/gif", "image/bmp"];
if (items.length > 1 || items["0"].kind != "file" || items["0"].type == "" || allowedTypes.indexOf(items["0"].type) == -1) {
    //Type not allowed
}

リファレンス: https://developer.mozilla.org/it/docs/Web/API/DataTransferItem

2
user2272143

私がMDNで読んだことから、あなたがしていることは正しいです。

MDNは、text-htmlなどの 推奨されるドラッグタイプ をリストしますが、適切なものがない場合は、 'text/html'タイプを使用してidをテキストとして保存するか、 ' application/node-id '。

0
Jules