80

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

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

イベントを発生させる最後の要素であると想定できることはわかってdragstartいますが... マルチタッチ. e.dataTransfer.setDatafromを使用して一意の識別子をアタッチすることも試みましdragstartたが、明らかにそのデータは/からアクセスできませんdragoverdragenter

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

それで、何かアイデアはありますか?

更新:この記事の執筆時点では、HTML5 のドラッグ アンド ドロップはどの主要なモバイル ブラウザーにも実装されていないようであり、実際にはマルチタッチについての主張は意味がありません。ただし、複数の要素を同時にドラッグすることを妨げないように見える仕様の実装全体で動作することが保証されているソリューションが必要です。

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

4

9 に答える 9

77

ここを通り過ぎるすべての人に明らかなように、ここに非常に明確な答えを追加したかった. 他の回答で何度か言われていますが、ここでは、私ができる限り明確にしています:

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

この情報は、DRAG_START および DRAG_END (ドロップ) 中にのみ使用できます。

問題は、仕様やここのような場所を十分に深く読むまで、まったく明白ではなく、腹立たしいことです.

回避策:

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

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

そして、そのようなこと。明確にするために、「含む」ことは特効薬ではなく、それ自体がパフォーマンスの問題になる可能性があります。使用方法とシナリオを理解するように注意してください。

于 2014-05-01T20:36:59.710 に答える
25

私の質問に対する簡単な答えは次のとおりです。いいえ。WHATWG 仕様dragenterでは、 、dragover、またはdragleaveイベントでドラッグされている要素 (仕様では「ソース ノード」と呼ばれます) への参照が提供されていません。

なぜだめですか?2 つの理由:

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

次に、HTML ドラッグ アンド ドロップを使用すると、複数のドキュメント間で要素をドラッグできます。これは素晴らしいことですが、すべてdragenterの 、dragover、またはdragleaveイベントでドラッグされる要素への参照を提供することは意味がないことも意味します。別のドキュメントの要素を参照することはできません。ドラッグが同じドキュメントで発生したか別のドキュメントで発生したかに関係なく、これらのイベントが同じように機能することが API の強みです。

しかし、(私の実用的なソリューションdataTransfer.typesの回答で説明されているように)スルーを除いて、すべてのドラッグイベントにシリアル化された情報を提供できないことは、API の明らかな省略です。ドラッグ イベントでの公開データの提案を WHATWG に提出しました。支持を表明していただければ幸いです。

于 2012-06-25T16:29:30.670 に答える
14

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

ここでアクティブな行は次のとおりです。

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

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

これは醜い、醜いハックです。もし誰かが私にもっと良い解決策を教えてくれたら、私は喜んで報奨金を与えます。

更新:追加する価値のある警告の 1 つは、洗練されていないことに加えて、仕様にはすべてのデータ型が小文字の ASCII に変換されると記載されていることです。そのため、大文字または Unicode を含むセレクターは機能しないことに注意してください。Jeffery のソリューションは、この問題を回避します。

于 2012-06-18T19:21:26.083 に答える
6

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

「(非常にエレガントでない)ソリューション」(デモ)の拡張:

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

    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);
    
  • ハンドラーで、データ型の中からドラッグIDを見つけ、dragenterグローバルハッシュから元の要素を取得します。

    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にアクセスできても、元の要素を見つけることができなくなります。

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


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

于 2012-06-20T18:07:34.973 に答える
3

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

var draggedElement = null;

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

function dragEnter(event) {
    // use the dragged element here...
};
于 2012-06-18T18:53:38.243 に答える
3

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

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

dragenterandイベントで、dragleaveexpando プロパティ値が現在のイベントのevent.xand と一致する要素を見つけます。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/

編集:私が読んだことから、HTML5ドラッグ可能はどのタッチデバイスでもサポートされているようには見えません. 任意のタッチ デバイスでドラッグ可能な動作を取得できますか? そうでない場合、マルチタッチは問題にならず、ドラッグされた要素を変数に格納するだけに頼ることができます。

于 2012-06-19T02:06:35.333 に答える
-1

http://help.dottoro.com/ljogqtqm.phpに電話することで入手できると思いますe.relatedTarget

OK、試してみましたが、うまくいきました。http e.target.previousElementSibling //jsfiddle.net/Gz8Qw/4/イベントが2回発生しているため、ハングアップしたと思います。1回はdiv用、もう1回はテキストノード用です(テキストノードに対して起動する場合は)。それがあなたがなりたい場所にあなたを連れて行くかどうかわからない...undefined

于 2012-06-18T18:41:37.590 に答える