4

ファイルのドラッグ/ドロップ ハンドラーを作成しようとしています (ファイルをブラウザー ウィンドウにドラッグして、アップロードに使用します)。

何らかの理由で、ドラッグ/ドロップ リスナーを本体で$("body")はなくにバインドする$("div")と、イベントが連続して数回発生し、場合によってはノンストップ (一見ループ) になることもあります。何が原因でしょうか?

コードの縮小版を次に示します: http://jsfiddle.net/WxMwK/9/

var over = false;

$("body")
    .on("dragover", function(e){
        e.preventDefault();
        if (! over) {
            over = true;
            $("ul").append($("<li/>").text("dragover"));    
        }
    })
    .on("dragleave", function(e){
        e.preventDefault();
        if (over) {
            over = false;
            $("ul").append($("<li/>").text("dragleave"));
        }
    })
    .on("drop", function(e){
        e.preventDefault();
        if (over) {
            over = false;
            $("ul").append($("<li/>").text("drop"));
        }
    }); 

テストするには: ファイルをオレンジ色の領域にドラッグすると、イベントが連続して複数回発生することがわかります。

4

4 に答える 4

13

アノンは(ほとんど)正しいです。簡単に言うと、マウスがドロップ ターゲット内の要素の端に移動すると、カーソルの下にある要素のと、以前にカーソルの下にあったdropenter要素のが取得されます。これは、絶対にすべての子孫に発生します。dropleave

に関連付けられている要素を確認することはできませんdragleave。マウスドロップ ターゲットから子要素に移動するdropenterと、子の が取得され、次にdropleaveターゲットの が取得されるためです。ちょっとばかげていて、これがどのように便利なデザインであるかがまったくわかりません。

これは、私が少し前に思いついた、くだらないjQueryベースのソリューションです。

var $drop_target = $(document.body);
var within_enter = false;

$drop_target.bind('dragenter', function(evt) {
    // Default behavior is to deny a drop, so this will allow it
    evt.preventDefault();

    within_enter = true;
    setTimeout(function() { within_enter = false; }, 0);

    // This is the part that makes the drop area light up
    $(this).addClass('js-dropzone');
});
$drop_target.bind('dragover', function(evt) {
    // Same as above
    evt.preventDefault();
});
$drop_target.bind('dragleave', function(evt) {
    if (! within_enter) {
        // And this makes it un-light-up  :)
        $(this).removeClass('js-dropzone');
    }
    within_enter = false;
});

// Handle the actual drop effect
$drop_target.bind('drop', function(evt) {
    // Be sure to reset your state down here
    $(this).removeClass('js-dropzone');
    within_enter = false;

    evt.preventDefault();

    do_whatever(evt.originalEvent.dataTransfer.files);
});

このトリックは、次の 2 つの事実に依存しています。

  • マウスを孫から子に移動すると、dragenterとの両方dragleaveがターゲット要素のキューに入れられます—<em>この順序で。
  • dragenterとは一緒dragleaveにキューに入れられます。

これが何が起こるかです。

  • イベントではdragenter、ドラッグの動きがまだ解決されていないことを示す共有変数を設定しました。
  • setTimeoutその変数をすぐに元に戻すために、ゼロの遅延で使用します。
  • しかし!2 つのイベントはまったく同時にキューに入れられるため、両方のイベントの解決が完了するまで、ブラウザーはスケジュールされた機能を実行しません。次に起こるのはdragleaveのイベント ハンドラです。
  • 同じターゲット要素で とdragleaveペアになっていることがわかった場合、それは、マウスがある子孫から別の子孫に移動したに違いないことを意味します。それ以外の場合、マウスは実際にターゲット要素から離れています。dragenter
  • その後、setTimeoutfinally は 0 秒後に解決され、別のイベントが発生する前に変数を元に戻します。

もっと簡単なアプローチは考えられません。

于 2013-01-18T05:13:50.580 に答える
0

、およびのBODYHTMLElement にリスナーを追加しています。dragoverdragleavedrop

DIV をドラッグし続けるとdragleave、マウスが BODY ではなく DIV をドラッグしているため、 が発生します。

第 2 に、 でバブル イベントを停止していないためDIV(リスナーが設定されていない)、 で起動されたドラッグオーバーDIVが にバブルしていBODYます。

再開した場合:

マウスがボディに入ります(ドラッグオーバー中)

-->ファイア ドラッグ オーバー (ボディ)

マウスは本体の DIV に入ります

-->ファイアドラグリーブ(BODYの)

--> (DIV の) ファイア ドラッグ-->イベント バブリング--> (BODY の) ファイア ドラッグ

と にも同様の問題がmouseoverあり、とmouseoutを使用することで修正されます。mouseentermouseleave

dragenterイベントタイプを使用して同じコードを試すことができるかもしれません。機能しない場合は、 が であるかどうかを確認できevent.targetますBODY。このテストは、望ましくないドラッグ イベントをスキップするのに役立ちます。

幸運を

于 2013-01-18T05:00:56.537 に答える