1. ホーム
  2. jquery

[解決済み] 子要素の上にドラッグすると、親要素の 'dragleave' が発生する

2022-04-20 03:04:47

質問

概要

以下のようなHTML構造になっているので、その上に dragenterdragleave イベントを <div id="dropzone"> 要素を使用します。

<div id="dropzone">
    <div id="dropzone-content">
        <div id="drag-n-drop">
            <div class="text">this is some text</div>
            <div class="text">this is a container with text and images</div>
        </div>
    </div>
</div>

問題点

の上にファイルをドラッグすると <div id="dropzone"> を使用します。 dragenter イベントは期待通りに発生します。しかし、マウスを子要素の上に移動させると、例えば <div id="drag-n-drop"> を実行すると dragenter イベントが発生します。 <div id="drag-n-drop"> 要素で、その後に dragleave イベントが発生します。 <div id="dropzone"> 要素を使用します。

にカーソルを合わせると <div id="dropzone"> 要素で、もう一度 dragenter イベントが再び発生し、それはそれでクールなのですが、その後に dragleave イベントが左の子要素に対して発生するので removeClass という命令が実行されてしまい、カッコ悪いです。

この動作が問題となる理由は2つあります。

  1. を付けるだけです。 dragenter &です。 dragleave から <div id="dropzone"> ということで、なぜ子要素にもこれらのイベントが付随しているのか理解できません。

  2. の上にドラッグしたままです。 <div id="dropzone"> 要素の子要素にカーソルを合わせているときに dragleave を発射してください。

jsFiddle

ここで、jsFiddleをいじってみましょう。 http://jsfiddle.net/yYF3S/2/

質問

ファイルをドラッグして <div id="dropzone"> 要素を使用します。 dragleave は、子要素の上をドラッグしていても発生しません...。 <div id="dropzone"> 要素の境界内の任意の場所でホバーリング/ドラッグしてください。 はいけません。 をトリガーします。 dragleave イベントが発生します。

少なくともHTML5のドラッグ&ドロップに対応しているブラウザでは、クロスブラウザ対応にする必要があるので、次のようにします。 この答え は適切ではありません。

GoogleやDropboxはこれを理解しているようですが、彼らのソースコードはminified/complexなので、彼らの実装からこれを理解することはできていません。

解決方法は?

ようやく満足のいく解決策を見つけました。実は、欲しいものを実現する方法をいくつか見つけたのですが、どれも今回の解決策ほどうまくはいきませんでした。 #dropzone 別のものでは、ブラウザからカーソルを離してもボーダーが消えないというものでした。

とにかく、私の一番のハックションはこれです。

var dragging = 0;

attachEvent(window, 'dragenter', function(event) {

    dragging++;
    $(dropzone).addClass('drag-n-drop-hover');

    event.stopPropagation();
    event.preventDefault();
    return false;
});

attachEvent(window, 'dragover', function(event) {

    $(dropzone).addClass('drag-n-drop-hover');

    event.stopPropagation();
    event.preventDefault();
    return false;
});

attachEvent(window, 'dragleave', function(event) {

    dragging--;
    if (dragging === 0) {
        $(dropzone).removeClass('drag-n-drop-hover');
    }

    event.stopPropagation();
    event.preventDefault();
    return false;
});

これはかなりうまくいくのですが、Firefoxでは問題が発生しました。 dragenter ということで、カウンタがずれてしまいました。しかし、それにもかかわらず、それは非常にエレガントなソリューションではありません。

そんな時、こんな質問を偶然見つけました。 Firefox でウィンドウの外側をドラッグしたときに dragleave イベントを検出する方法

そこで、私は 答え と、自分の状況に当てはめて考えてみました。

$.fn.dndhover = function(options) {

    return this.each(function() {

        var self = $(this);
        var collection = $();

        self.on('dragenter', function(event) {
            if (collection.size() === 0) {
                self.trigger('dndHoverStart');
            }
            collection = collection.add(event.target);
        });

        self.on('dragleave', function(event) {
            /*
             * Firefox 3.6 fires the dragleave event on the previous element
             * before firing dragenter on the next one so we introduce a delay
             */
            setTimeout(function() {
                collection = collection.not(event.target);
                if (collection.size() === 0) {
                    self.trigger('dndHoverEnd');
                }
            }, 1);
        });
    });
};

$('#dropzone').dndhover().on({
    'dndHoverStart': function(event) {

        $('#dropzone').addClass('drag-n-drop-hover');

        event.stopPropagation();
        event.preventDefault();
        return false;
    },
    'dndHoverEnd': function(event) {

        $('#dropzone').removeClass('drag-n-drop-hover');

        event.stopPropagation();
        event.preventDefault();
        return false;
    }
});

これはクリーンでエレガントで、これまで私がテストしたすべてのブラウザで動作しているようです(IEはまだテストしていません)。