子要素にカーソルを合わせるとHTML5 dragleaveが発生する


300

私が抱えている問題dragleaveは、要素の子要素をホバーすると要素のイベントが発生することです。また、dragenter親要素を再びホバーするときにも発生しません。

簡単なフィドルを作成しました:http : //jsfiddle.net/pimvdb/HU6Mk/1/

HTML:

<div id="drag" draggable="true">drag me</div>

<hr>

<div id="drop">
    drop here
    <p>child</p>
    parent
</div>

次のJavaScriptを使用:

$('#drop').bind({
                 dragenter: function() {
                     $(this).addClass('red');
                 },

                 dragleave: function() {
                     $(this).removeClass('red');
                 }
                });

$('#drag').bind({
                 dragstart: function(e) {
                     e.allowedEffect = "copy";
                     e.setData("text/plain", "test");
                 }
                });

divそこに何かをドラッグすると、ドロップを赤くしてユーザーに通知します。これは機能しますが、p子にドラッグすると、dragleaveが起動され、divはもう赤ではありません。ドロップに戻ってdivも、再び赤くなることはありません。div赤にするためには、ドロップから完全に移動し、再度ドラッグしてドロップする必要があります。

dragleave子要素にドラッグするときに発火を防ぐことは可能ですか?

2017年の更新: TL; DR、pointer-events: none;以下の@HDの回答に記載されているCSS を検索します。これは、最新のブラウザーとIE11で機能します。


報告されたバグpimvdbは2012年5月の時点でもWebkitに存在します。ドラッグオーバーにクラスを追加することでこれに対抗しました。
ajm

2
@ajm:ありがとう、それはある程度機能します。ただし、Chromeでは、子要素に出入りするときにフラッシュdragleaveが発生します。これは、おそらくその場合でも発生するため です。
pimvdb 2012年

私はjQuery UI をオープンしました。バグの投票は大歓迎です。そのため、リソースを投入することを決定できます
fguillen

@fguillen:すみませんが、これはjQuery UIとは関係ありません。実際、バグをトリガーするためにjQueryは必要ありません。私はすでにWebKitバグを提出しましたが、現時点でアップデートはありません。
pimvdb 2012

2
@pimvdb HDの答えを受け入れませんか?
TylerH 2017

回答:


338

参照カウンターを保持し、dragenterを取得したときにインクリメントし、dragleaveを取得したときにデクリメントするだけです。カウンターが0の場合-クラスを削除します。

var counter = 0;

$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault(); // needed for IE
        counter++;
        $(this).addClass('red');
    },

    dragleave: function() {
        counter--;
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    }
});

注:ドロップイベントで、カウンターをゼロにリセットし、追加したクラスをクリアします。

ここで実行できます


8
OMGこれは最も明白な解決策であり、投票は1つしかありませんでした。私はそれについて考えていましたが、最初のいくつかの回答の高度さのレベルを見て、それをほとんど破棄しました。欠点はありましたか?
アーサーCorenzan 2014

11
ドラッグされている要素の端が別のドラッグ可能な要素の端に接触している場合、これは機能しませんでした。たとえば、ソート可能なリスト。要素を下にドラッグすると、次のドラッグ可能なアイテムの上ではカウンターが0に戻りませんが、代わりに1で止まります。しかし、他のドラッグ可能な要素の外に横にドラッグすると、機能します。私はpointer-events: none子供たちと一緒に行きました。ドラッグを開始すると、このプロパティを持つクラスを追加し、ドラッグが終わるとクラスを削除します。SafariとChromeではうまく機能しましたが、Firefoxではうまくいきませんでした。
Arthur Corenzan 2015年

13
これは、Firefox以外のすべてのブラウザで機能しました。私の場合はどこでも機能する新しいソリューションがあります。最初dragenterevent.currentTarget、新しい変数に保存しますdragEnterTargetdragEnterTarget設定されている限りdragenter、子供からのイベントであるため、それ以上のイベントは無視します。すべてのdragleaveイベントでチェックしdragEnterTarget === event.targetます。これがfalseの場合、子によって起動されたため、イベントは無視されます。これに該当する場合は、にリセットdragEnterTargetundefinedます。
Pipo 2016年

3
素晴らしいソリューション。ただし、ドロップの受け入れを処理する関数でカウンターを0にリセットする必要がありました。そうしないと、後続のドラッグが期待どおりに機能しませんでした。
Liam Johnston

2
@Woodyコメントの膨大なリストに彼のコメントはありませんでしたが、はい、それが修正です。それを回答に組み込んでみませんか?
BT

93

子要素にドラッグするときにdragleaveが起動しないようにすることはできますか?

はい。

#drop * {pointer-events: none;}

そのCSSはChromeには十分であるようです。

Firefoxで使用する場合、#dropには直接テキストノードを含めないでください(そうでなければ、要素が「それ自体に任せる」という奇妙な問題があります)。そのため、要素を1つだけ残しておくことをお勧めします(たとえば、内部でdivを使用します) #dropですべてを中に入れます)

これは、元の質問(壊れた)の例を解決するjsfiddleです。

@Theodore Brownの例から分岐した簡略版も作成しましたが、このCSSのみに基づいています。

ただし、すべてのブラウザーにこのCSSが実装されているわけではありません。http//caniuse.com/pointer-events

Facebookのソースコードを見ると、これをpointer-events: none;何度か見つけることができましたが、おそらく、グレースフルデグラデーションのフォールバックと一緒に使用されています。少なくともそれはとても簡単で、多くの環境の問題を解決します。


pointer-eventsプロパティは今後の正しい解決策ですが、残念ながら、まだ広く使用されているIE8〜IE10では機能しません。また、現在のjsFiddleはIE11でも機能しないことを指摘しておきます。これは、必要なイベントリスナーとデフォルトの動作防止が追加されていないためです。
セオドアブラウン

2
ポインタイベントを使用することは確かに良い答えです。自分で見つける前に少し苦労しましたが、その答えはもっと高いはずです。
フロリオン2014

古いブラウザをサポートする必要がないほど幸運な私たちにとっては素晴らしいオプションです。
Luke Hansford、2015年

7
子供がボタンの場合はどうなりますか?oO
デビッド

9
このソリューションもブロックするため、エリア内に他のコントローラー要素(編集、削除)がない場合にのみ機能します。
ZoltánSüleOct

45

ここでは、最も単純なクロスブラウザソリューション(真剣に):

jsfiddle <-ボックス内にファイルをドラッグしてみてください

あなたはそのようなことをすることができます:

var dropZone= document.getElementById('box');
var dropMask = document.getElementById('drop-mask');

dropZone.addEventListener('dragover', drag_over, false);
dropMask.addEventListener('dragleave', drag_leave, false);
dropMask.addEventListener('drop', drag_drop, false);

簡単に言うと、ドロップゾーン内に「マスク」を作成し、幅と高さを継承し、絶対位置を指定して、ドラッグオーバーが開始したときに表示されるようにします。
そのため、そのマスクを表示した後、他のドラッグリーブアンドドロップイベントをそのマスクにアタッチすることでトリックを実行できます。

去るか、または落ちた後、あなたは再びマスクを単に隠すだけです。
シンプルで合併症なし。

(オブザース:Greg Pettitのアドバイス-マスクが境界線を含むボックス全体にホバリングすることを確認する必要があります)


理由は不明ですが、Chromeで一貫して機能していません。領域を離れると、マスクが表示されたままになることがあります。
Greg Pettit 2013年

2
実はそれが境界です。マスクを境界線と重ねて、独自の境界線がないようにします。これで問題なく動作するはずです。
Greg Pettit 2013年

1
jsfiddleにバグがあることに注意してください。drag_dropで、「#box-a」ではなく「#box」のホバークラスを削除する必要があります
エントロピー

これは素晴らしい解決策ですが、どういうわけか私にはうまくいきませんでした。ようやく回避策を見つけました。他の何かを探しているあなたのために。あなたはこれを試すことができます:github.com/bingjie2680/jquery-draghover
bingjie2680

いいえ。子要素の制御を失いたくありません。私は、私たちのために仕事をしている問題に対処する単純なjQueryプラグインを作成しました。私の答えを確認してください
Luca Fagioli、2015

39

この質問が尋ねられてからかなり時間が経ち、多くの解決策(醜いハックを含む)が提供されました。

私はこの回答の回答のおかげで、私が最近抱えていた同じ問題をなんとか解決し、このページにアクセスした人に役立つかもしれないと思いました。全体的なアイデアは、格納することであるevenet.targetondrageenter毎回それは親または子要素のいずれかで呼び出されます。次にondragleave、現在のターゲット(event.target)がに保存したオブジェクトと等しいかどうかを確認しondragenterます。

これら2つが一致する唯一のケースは、ドラッグがブラウザーウィンドウから離れたときです。

これが正常に機能する理由は、マウスが要素を離れ(たとえばel1)、別の要素(たとえばel2)に入ると、最初にel2.ondragenterが呼び出され、次にが呼び出されel1.ondragleaveます。ドラッグがブラウザウィンドウに入る/離れるされた場合にのみ、event.targetとなります''両方にel2.ondragenterel1.ondragleave

これが私の実際のサンプルです。IE9 +、Chrome、Firefox、Safariでテストしました。

(function() {
    var bodyEl = document.body;
    var flupDiv = document.getElementById('file-drop-area');

    flupDiv.onclick = function(event){
        console.log('HEy! some one clicked me!');
    };

    var enterTarget = null;

    document.ondragenter = function(event) {
        console.log('on drag enter: ' + event.target.id);
        enterTarget = event.target;
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-drag-on-top';
        return false;
    };

    document.ondragleave = function(event) {
        console.log('on drag leave: currentTarget: ' + event.target.id + ', old target: ' + enterTarget.id);
        //Only if the two target are equal it means the drag has left the window
        if (enterTarget == event.target){
            event.stopPropagation();
            event.preventDefault();
            flupDiv.className = 'flup-no-drag';         
        }
    };
    document.ondrop = function(event) {
        console.log('on drop: ' + event.target.id);
        event.stopPropagation();
        event.preventDefault();
        flupDiv.className = 'flup-no-drag';
        return false;
    };
})();

そして、これはシンプルなhtmlページです:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Multiple File Uploader</title>
<link rel="stylesheet" href="my.css" />
</head>
<body id="bodyDiv">
    <div id="cntnr" class="flup-container">
        <div id="file-drop-area" class="flup-no-drag">blah blah</div>
    </div>
    <script src="my.js"></script>
</body>
</html>

私が行った適切なスタイル設定では、ユーザーがファイルを適切な場所に簡単にドロップできるように、ファイルが画面にドラッグされるたびに内側のdiv(#file-drop-area)を大きくすることです。


4
これは最良のソリューションであり、カウンターよりも優れており(特にイベントを委任する場合)、ドラッグ可能な子でも機能します。
フレッド

3
これは、dragenterイベントの重複の問題の解決には役立ちません
BT

これは、CSS疑似要素または疑似クラスである「子」に対して機能しますか?私はそれを得ることができませんでしたが、多分私はそれを間違っていました。
ジョシュブリーチャースナイダー

1
2020年3月の時点で、私もこのソリューションで最高の幸運を手に入れました。注:一部のリンターは==over の使用について不満を言うかもしれません===。イベントターゲットオブジェクト参照を比較しているので、===うまく機能します。
アレックス

33

この問題を解決する「正しい」方法は、ドロップターゲットの子要素のポインターイベントを無効にすることです(@HDの回答のように)。これは私が作成したjsFiddleで、この手法を示しています。残念ながら、IE 11より前のバージョンのInternet Explorerではポインターイベントをサポートしいないため、これは機能しません。

幸いなことに、旧バージョンのIE で機能する回避策を見つけることができました。基本的に、dragleave子要素の上にドラッグしたときに発生するイベントを識別して無視します。のでdragenter、イベントが前の子ノードに解雇されたdragleave親のイベント、個別のイベントリスナーを追加またはドロップターゲットから「無視ドラッグ脱退」クラスを削除し、それぞれの子ノードに追加することができます。次に、ドロップターゲットのdragleaveイベントリスナーは、このクラスが存在するときに発生する呼び出しを単に無視できます。ここだこの回避策を実証jsFiddleが。テスト済みで、Chrome、Firefox、IE8 +で動作します。

更新:

私が作成した統合ソリューションを実証jsFiddleをサポートしている場合、ポインタイベントが使用されている特徴検出を使用して、(現在はクロム、Firefox、およびIE11)を、ポインタイベントのサポートが利用できない場合、ブラウザは、バックの子ノードにイベントを追加することにフォール(IE8 -10)。


この回答は、望ましくない起動の回避策を示していますが、「子要素にドラッグするときにdragleaveが起動しないようにすることは可能ですか?」という質問を無視しています。完全に。
HD

ブラウザの外部からファイルをドラッグすると、動作がおかしくなることがあります。Firefoxでは、「子」の入力->親の入力->子の離脱->子の入力->子の離脱 "があり、親から離れることはできません。古いIEでは、addEventListenerのattachEvent置換が必要でした。
HD

その解決策はバブルに強く依存します。多くの人々はそれを知らない可能性があるため、すべてのaddEventListenerの「false」は必須であると強調する必要があります(これはデフォルトの動作です)。
HD

その単一のドラッグ可能オブジェクトは、ドロップゾーンにドラッグスタートイベントをトリガーしない他のドラッグ可能なオブジェクトにドロップゾーンが使用されたときに表示されない効果をドロップゾーンに追加します。おそらく、他のハンドラーで実際のドロップゾーンを維持しながら、ドラッグの効果のためにすべてをドロップゾーンとして使用することでそれが可能になります。
HD

1
@HD dragleaveChrome、Firefox、およびIE11 +でイベントが発生しないように、pointer-events CSSプロパティを使用する方法に関する情報で回答を更新しました。IE10に加えて、IE8とIE9をサポートするように他の回避策も更新しました。「Drag me」リンクをドラッグしたときにのみ、ドロップゾーン効果が追加されることが意図されています。他のユーザーは、ユースケースをサポートするために必要に応じてこの動作を自由に変更できます。
セオドアブラウン

14

問題はdragleave、マウスが子要素の前に移動したときにイベントが発生していることです。

e.target要素が要素と同じかどうかを確認するためにさまざまな方法を試しましたthisが、改善はありませんでした。

この問題を修正する方法は少しハックでしたが、100%機能します。

dragleave: function(e) {
               // Get the location on screen of the element.
               var rect = this.getBoundingClientRect();

               // Check the mouseEvent coordinates are outside of the rectangle
               if(e.x > rect.left + rect.width || e.x < rect.left
               || e.y > rect.top + rect.height || e.y < rect.top) {
                   $(this).removeClass('red');
               }
           }

1
ありがとう!ただし、これをChromeで機能させることはできません。あなたのハックの実際のフィドルを提供できますか?
pimvdb 2012

3
コーディネートもチェックしてやろうと思っていました。あなたは私のためにほとんどの仕事をしました、thx :)。私はしかしいくつかの調整をしなければならなかった:if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
クリストフ2013

1
イベントにはe.xand がないため、Chromeでは機能しませんe.y
Hengjie 2013

私はこのソリューションが好きです。最初はFirefoxで動作しませんでした。しかし、exをe.clientXに、eyをe.clientYに置き換えれば機能します。Chromeでも動作します。
Nicole Stutz 2013年

私にとって
Chrome

14

HTML5を使用している場合は、親のclientRectを取得できます。

let rect = document.getElementById("drag").getBoundingClientRect();

次に、parent.dragleave()で:

dragleave(e) {
    if(e.clientY < rect.top || e.clientY >= rect.bottom || e.clientX < rect.left || e.clientX >= rect.right) {
        //real leave
    }
}

ここにjsfiddleがあります


2
すばらしい答えです。
broc.seib 2018年

バディに感謝します。私は単純なjavascriptアプローチが好きです。
Tim Gerhard、

を持つ要素で使用する場合border-radius、ポインタをコーナーの近くに移動すると、実際には要素が残りますが、このコードはまだ内部にいると見なします(要素は残しましたが、境界の四角形のままです)。その後、dragleaveイベントハンドラーはまったく呼び出されません。
ジェイダダニア

11

非常に簡単な解決策は、 pointer-events CSSプロパティことです。ジャストは、その値を設定するnoneときにはdragstartすべての子要素に。これらの要素は、マウス関連のイベントをトリガーしなくなります。そのため、これらの要素は、それらの上でマウスをキャッチしないため、親でドラッグリーブをトリガーしません。

このプロパティをに戻すことを忘れないでください autoドラッグを終了ときに、 ;)


11

イベントが各ドラッグ要素に個別にアタッチされていると仮定すると、これまでのところ、このかなり単純なソリューションがうまく機能しています。

if (evt.currentTarget.contains(evt.relatedTarget)) {
  return;
}

これは素晴らしい!@kennethを共有してくれてありがとう、あなたは私の日を救った!他の多くの回答は、1つのドロップ可能ゾーンがある場合にのみ適用されます。
antoni

私にとっては、ChromeとFirefoxで機能しますが、IDはEdgeでは機能しません。ご覧ください:jsfiddle.net/iwiss/t9pv24jo
iwis

8

非常にシンプルなソリューション:

parent.addEventListener('dragleave', function(evt) {
    if (!parent.contains(evt.relatedTarget)) {
        // Here it is only dragleave on the parent
    }
}

これはSafari 12.1では機能しないようです。jsfiddle.net
Adam Taylor

7

jQueryソースコードから少しインスピレーションを得て、Firefoxで修正できます

dragleave: function(e) {
    var related = e.relatedTarget,
        inside = false;

    if (related !== this) {

        if (related) {
            inside = jQuery.contains(this, related);
        }

        if (!inside) {

            $(this).removeClass('red');
        }
    }

}

残念ながらrelatedTargetdragleaveイベントには存在しないように見えるため、Chromeでは機能しません。また、Firefoxでは例が機能しなかったため、Chromeで機能していると思います。 上記のコードを実装したバージョンを次に示します。


おかげでたくさんの、しかし確かにそれのChrome私はこの問題を解決しようとしている。
pimvdb

5
@pimvdb バグを記録しことがわかりました。誰かがこの回答に遭遇した場合に備えて、ここに参照を残しておきます。
robertc '19

確かにそうしましたが、ここにリンクを追加するのを忘れていました。それをしてくれてありがとう。
pimvdb 2011

それまでの間、Chromeのバグは修正されました。
2017

7

そして、これがChromeのソリューションです。

.bind('dragleave', function(event) {
                    var rect = this.getBoundingClientRect();
                    var getXY = function getCursorPosition(event) {
                        var x, y;

                        if (typeof event.clientX === 'undefined') {
                            // try touch screen
                            x = event.pageX + document.documentElement.scrollLeft;
                            y = event.pageY + document.documentElement.scrollTop;
                        } else {
                            x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
                            y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
                        }

                        return { x: x, y : y };
                    };

                    var e = getXY(event.originalEvent);

                    // Check the mouseEvent coordinates are outside of the rectangle
                    if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
                        console.log('Drag is really out of area!');
                    }
                })

«if(typeof event.clientX === 'undefined')»に入りますか?
Aldekein

1
うまく機能しましたが、ブラウザ上に別のウィンドウがある可能性があるため、マウスの位置を取得して長方形の画面領域と比較するだけでは不十分です。
HD

@HDにも同意します。また、border-radius上記の@azlarの回答に関する私のコメントで説明したように、要素が大きい場合に問題が発生します。
ジェイダダニア

7

document.elementFromPointを使用した別のソリューションを次に示します。

 dragleave: function(event) {
   var event = event.originalEvent || event;
   var newElement = document.elementFromPoint(event.pageX, event.pageY);
   if (!this.contains(newElement)) {
     $(this).removeClass('red');
   }
}

これがうまくいくことを願って、ここにフィドルがあります。


3
これは、そのままでは機能しませんでした。event.clientX, event.clientYそれらはページではなくビューポートに関連しているので、代わりに使用する必要がありました。それが他の失われた魂を助けることを願っています。
Jon Abrams

7

簡単な解決策は、CSSルールpointer-events: noneを子コンポーネントに追加して、のトリガーを防止することですondragleave。例を参照してください:

function enter(event) {
  document.querySelector('div').style.border = '1px dashed blue';
}

function leave(event) {
  document.querySelector('div').style.border = '';
}
div {
  border: 1px dashed silver;
  padding: 16px;
  margin: 8px;
}

article {
  border: 1px solid silver;
  padding: 8px;
  margin: 8px;
}

p {
  pointer-events: none;
  background: whitesmoke;
}
<article draggable="true">drag me</article>

<div ondragenter="enter(event)" ondragleave="leave(event)">
  drop here
  <p>child not triggering dragleave</p>
</div>


私にも同じ問題があり、あなたの解決策はうまく機能しました。他の人へのヒント、これは私のケースでした:ドラッグイベントを無視しようとしている子要素がドラッグされている要素のクローンである場合(視覚的なプレビューを実現しようとしている)、クローンの作成時にこれをdragstart内で使用できます:dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
Scaramouche

4

このクロスブラウザかどうかはわかりませんが、Chromeでテストすると問題が解決します。

ページ全体にファイルをドラッグアンドドロップしたいのですが、子要素の上にドラッグすると、dragleaveが起動します。私の修正はマウスのxとyを見ることでした:

ページ全体をオーバーレイするdivがあります。ページが読み込まれると、非表示になります。

ドキュメントをドラッグすると表示され、親にドロップすると処理されます。親を離れると、xとyがチェックされます。

$('#draganddrop-wrapper').hide();

$(document).bind('dragenter', function(event) {
    $('#draganddrop-wrapper').fadeIn(500);
    return false;
});

$("#draganddrop-wrapper").bind('dragover', function(event) {
    return false;
}).bind('dragleave', function(event) {
    if( window.event.pageX == 0 || window.event.pageY == 0 ) {
        $(this).fadeOut(500);
        return false;
    }
}).bind('drop', function(event) {
    handleDrop(event);

    $(this).fadeOut(500);
    return false;
});

1
ハッキーだが賢い。好き。私のために働いた。
cupcakekid

1
ああ、どうすればこの回答の投票数が増えますか!ありがとう
Kirby

4

この正確な問題を処理するためにDragsterと呼ばれる小さなライブラリを作成しましたが、IEで何も通知せずに何もしません(DOMイベントコンストラクターはサポートしていませんが、jQueryのカスタムイベントを使用して同様のことを書くのは簡単です)。


非常に便利です(少なくとも、私がChromeだけに関心がある私にとっては)。
M Katz

4

私は同じ問題を抱えていて、pk7sソリューションを使用しようとしました。それは機能しましたが、余分なdom要素がなくても少し良くできました。

基本的には考え方は同じです-ドロップ可能な領域の上に追加の非表示オーバーレイを追加します。余分なdom要素なしでこれを行うことができます。ここで、CSS疑似要素が役立った部分を示します。

JavaScript

var dragOver = function (e) {
    e.preventDefault();
    this.classList.add('overlay');
};

var dragLeave = function (e) {
    this.classList.remove('overlay');
};


var dragDrop = function (e) {
    this.classList.remove('overlay');
    window.alert('Dropped');
};

var dropArea = document.getElementById('box');

dropArea.addEventListener('dragover', dragOver, false);
dropArea.addEventListener('dragleave', dragLeave, false);
dropArea.addEventListener('drop', dragDrop, false);

CSS

この後のルールは、ドロップ可能な領域の完全に覆われたオーバーレイを作成します。

#box.overlay:after {
    content:'';
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    z-index: 1;
}

これが完全なソリューションです:http : //jsfiddle.net/F6GDq/8/

私はそれが同じ問題を持つ人を助けることを願っています。


1
時々chromeで機能しない(ドラッグリーブを適切にキャッチしない)
Timo Huovinen 2014年

4

ドラッグされた要素が子であるかどうかを確認し、子である場合は、「ドラッグオーバー」スタイルクラスを削除しないでください。かなりシンプルで私にとってはうまくいきます:

 $yourElement.on('dragleave dragend drop', function(e) {
      if(!$yourElement.has(e.target).length){
           $yourElement.removeClass('is-dragover');
      }
  })

私にとって最も簡単な解決策のように見え、それが私の問題を解決しました。ありがとう!
Francesco Marchetti-Stasi

3

私は同じ問題に遭遇しました、そしてここに私の解決策があります-私はそれが上記よりはるかに簡単だと思います。それがクロスブラウザかどうかはわかりません(バブルの順序にも依存する可能性があります)

簡単にするためにjQueryを使用しますが、ソリューションはフレームワークに依存しない必要があります。

イベントは、いずれかの方法で親にバブルします。

<div class="parent">Parent <span>Child</span></div>

イベントを添付

el = $('.parent')
setHover = function(){ el.addClass('hovered') }
onEnter  = function(){ setTimeout(setHover, 1) }
onLeave  = function(){ el.removeClass('hovered') } 
$('.parent').bind('dragenter', onEnter).bind('dragleave', onLeave)

そしてそれはそれについてです。:)子のonEnterが親のonLeaveの前に発生しても、順序を少し逆にするので遅延するため、クラスが最初に削除され、ミリ秒後に再適用されます。


このスニペットが行うことは、「ホバーされた」クラスが次のティックサイクルを再適用することによって削除されるのを防ぐことです(「ドラッグリーブ」イベントを無効にします)。
null

それは無駄ではありません。親から離れると、期待どおりに機能します。このソリューションの威力は、それがシンプルであることです。理想的ではなく、そこにあるのが最善ではありません。より良い解決策は、子供にエンターをマークし、オンリーブチェックで子供を入力したかどうかを確認し、そうでない場合はリーブイベントをトリガーすることです。テスト、追加の警備員、孫のためのチェスなどが必要になるでしょう
Marcin Raczkowski

3

私はとりわけ、この奇妙な振る舞いを修正する、ドリップドロップと呼ばれるドラッグアンドドロップモジュールを書きました。何かの基礎として使用できる優れた低レベルのドラッグアンドドロップモジュール(ファイルのアップロード、アプリ内のドラッグアンドドロップ、外部ソースとのドラッグ)を探している場合は、これを確認する必要がありますモジュールアウト:

https://github.com/fresheneesz/drip-drop

これは、あなたがドリップドロップでやろうとしていることをどのように行うかです:

$('#drop').each(function(node) {
  dripDrop.drop(node, {
    enter: function() {
      $(node).addClass('red')  
    },
    leave: function() {
      $(node).removeClass('red')
    }
  })
})
$('#drag').each(function(node) {
  dripDrop.drag(node, {
    start: function(setData) {
      setData("text", "test") // if you're gonna do text, just do 'text' so its compatible with IE's awful and restrictive API
      return "copy"
    },
    leave: function() {
      $(node).removeClass('red')
    }
  })
})

ライブラリなしでこれを行うには、カウンターテクニックをドリップドロップで使用します。最も高い評価の回答では、最初のドロップ以外のすべての点で問題が発生するような重要な手順がありません。これを正しく行う方法は次のとおりです。

var counter = 0;    
$('#drop').bind({
    dragenter: function(ev) {
        ev.preventDefault()
        counter++
        if(counter === 1) {
          $(this).addClass('red')
        }
    },

    dragleave: function() {
        counter--
        if (counter === 0) { 
            $(this).removeClass('red');
        }
    },
    drop: function() {
        counter = 0 // reset because a dragleave won't happen in this case
    }
});

2

もう少し簡単な代替作業ソリューション。

//Note: Due to a bug with Chrome the 'dragleave' event is fired when hovering the dropzone, then
//      we must check the mouse coordinates to be sure that the event was fired only when 
//      leaving the window.
//Facts:
//  - [Firefox/IE] e.originalEvent.clientX < 0 when the mouse is outside the window
//  - [Firefox/IE] e.originalEvent.clientY < 0 when the mouse is outside the window
//  - [Chrome/Opera] e.originalEvent.clientX == 0 when the mouse is outside the window
//  - [Chrome/Opera] e.originalEvent.clientY == 0 when the mouse is outside the window
//  - [Opera(12.14)] e.originalEvent.clientX and e.originalEvent.clientY never get
//                   zeroed if the mouse leaves the windows too quickly.
if (e.originalEvent.clientX <= 0 || e.originalEvent.clientY <= 0) {

これは、Chromeで常に機能するとは限りません。clientXマウスがボックスの外側にあると、0 を超えることがあります。確かに、私の要素はposition:absolute
Hengjie 2013

それは常に発生しますか、それとも時々発生しますか?マウスの動きが速すぎる場合(ウィンドウの外など)、誤った値が表示される可能性があるためです。
教授2013

それは90%の確率で発生します。まれなケース(10回に1回)で0に到達できる場合があります。もう一度ゆっくりマウスを動かしてみますが、すばやく動かしている(おそらく通常の速度)とは言えませんでした。
Hengjie 2013

2

非常に多くの時間を費やした後、私はその提案を意図したとおりに機能させました。ファイルがドラッグされ、ドキュメントがドラッグオーバーされたときにのみキューを提供したかったのですが、Chromeブラウザーでドラッグリーブが痛みを伴うちらつきを引き起こしていました。

これは私がそれを解決した方法であり、ユーザーにも適切な手掛かりを投げます。

$(document).on('dragstart dragenter dragover', function(event) {    
    // Only file drag-n-drops allowed, http://jsfiddle.net/guYWx/16/
    if ($.inArray('Files', event.originalEvent.dataTransfer.types) > -1) {
        // Needed to allow effectAllowed, dropEffect to take effect
        event.stopPropagation();
        // Needed to allow effectAllowed, dropEffect to take effect
        event.preventDefault();

        $('.dropzone').addClass('dropzone-hilight').show();     // Hilight the drop zone
        dropZoneVisible= true;

        // http://www.html5rocks.com/en/tutorials/dnd/basics/
        // http://api.jquery.com/category/events/event-object/
        event.originalEvent.dataTransfer.effectAllowed= 'none';
        event.originalEvent.dataTransfer.dropEffect= 'none';

         // .dropzone .message
        if($(event.target).hasClass('dropzone') || $(event.target).hasClass('message')) {
            event.originalEvent.dataTransfer.effectAllowed= 'copyMove';
            event.originalEvent.dataTransfer.dropEffect= 'move';
        } 
    }
}).on('drop dragleave dragend', function (event) {  
    dropZoneVisible= false;

    clearTimeout(dropZoneTimer);
    dropZoneTimer= setTimeout( function(){
        if( !dropZoneVisible ) {
            $('.dropzone').hide().removeClass('dropzone-hilight'); 
        }
    }, dropZoneHideDelay); // dropZoneHideDelay= 70, but anything above 50 is better
});

1

同様の問題がありました。bodyのdragleaveイベントでドロップゾーンを非表示にするためのコードが、子要素をホバーするとGoogle Chromeでドロップゾーンがちらつくように不規則に起動されました。

これをすぐに呼び出すのではなく、dropzoneを非表示にする関数をスケジュールすることで、これを解決できました。次に、別のドラッグオーバーまたはドラッグリーブが発生すると、スケジュールされた関数呼び出しがキャンセルされます。

body.addEventListener('dragover', function() {
    clearTimeout(body_dragleave_timeout);
    show_dropzone();
}, false);

body.addEventListener('dragleave', function() {
    clearTimeout(body_dragleave_timeout);
    body_dragleave_timeout = setTimeout(show_upload_form, 100);
}, false);

dropzone.addEventListener('dragover', function(event) {
    event.preventDefault();
    dropzone.addClass("hover");
}, false);

dropzone.addEventListener('dragleave', function(event) {
    dropzone.removeClass("hover");
}, false);

これも私がやったことですが、まだ大雑把です。
mpen 2016年

1

" ドラッグリーブ」イベントは、マウスポインターがターゲットコンテナーのドラッグ領域を出ると発生します。

多くの場合、親だけがドロップ可能であり、子孫はドロップできないので、これは非常に理にかなっています。event.stopPropogation()だと思いますこのケースを扱っているはずですが、それはトリックをしないように思えます。

上記のいくつかのソリューションはほとんどの場合に機能するようですが、iframeなどのdragenter / dragleaveイベントをサポートしない子の場合は失敗します。

1つの回避策は、event.relatedTargetを確認し、それがコンテナー内にあるかどうかを確認してから、ここで行ったようにdragleaveイベントを無視することです。

function isAncestor(node, target) {
    if (node === target) return false;
    while(node.parentNode) {
        if (node.parentNode === target)
            return true;
        node=node.parentNode;
    }
    return false;
}

var container = document.getElementById("dropbox");
container.addEventListener("dragenter", function() {
    container.classList.add("dragging");
});

container.addEventListener("dragleave", function(e) {
    if (!isAncestor(e.relatedTarget, container))
        container.classList.remove("dragging");
});

ここで実用的なフィドルを見つけることができます!


1

解決した..!

exの配列を宣言します。

targetCollection : any[] 

dragenter: function(e) {
    this.targetCollection.push(e.target); // For each dragEnter we are adding the target to targetCollection 
    $(this).addClass('red');
},

dragleave: function() {
    this.targetCollection.pop(); // For every dragLeave we will pop the previous target from targetCollection
    if(this.targetCollection.length == 0) // When the collection will get empty we will remove class red
    $(this).removeClass('red');
}

子要素について心配する必要はありません。


1

これらのすべての回答を読んだ後でも、これでLOTに苦労し、解決策をあなたと共有できると思いました。私の考えは、単にdragleaveイベントリスナーを完全に dragenterイベントが不必要に発生しないようにしながら、dragenterイベントが発生するたびにdragleave動作をコーディングすることでした。

以下の私の例では、テーブルがあり、ドラッグアンドドロップAPIを介してテーブル行のコンテンツを相互に交換できるようにしたいと思います。でdragenter、CSSクラスは、それを強調するために、あなたが現在あなたの要素をドラッグしていたに行要素に追加されなければならない、との上にdragleave、このクラスは削除されなければなりません。

例:

非常に基本的なHTMLテーブル:

<table>
  <tr>
    <td draggable="true" class="table-cell">Hello</td>
  </tr>
  <tr>
    <td draggable="true" clas="table-cell">There</td>
  </tr>
</table>

そして、各テーブルセルに追加DragEnterイベントハンドラ関数、(さておきdragstartdragoverdrop、およびdragendこの質問に固有のものではありませんハンドラ、ので、ここではコピーされません):

/*##############################################################################
##                              Dragenter Handler                             ##
##############################################################################*/

// When dragging over the text node of a table cell (the text in a table cell),
// while previously being over the table cell element, the dragleave event gets
// fired, which stops the highlighting of the currently dragged cell. To avoid
// this problem and any coding around to fight it, everything has been
// programmed with the dragenter event handler only; no more dragleave needed

// For the dragenter event, e.target corresponds to the element into which the
// drag enters. This fact has been used to program the code as follows:

var previousRow = null;

function handleDragEnter(e) {
  // Assure that dragenter code is only executed when entering an element (and
  // for example not when entering a text node)
  if (e.target.nodeType === 1) {
    // Get the currently entered row
    let currentRow = this.closest('tr');
    // Check if the currently entered row is different from the row entered via
    // the last drag
    if (previousRow !== null) {
      if (currentRow !== previousRow) {
        // If so, remove the class responsible for highlighting it via CSS from
        // it
        previousRow.className = "";
      }
    }
    // Each time an HTML element is entered, add the class responsible for
    // highlighting it via CSS onto its containing row (or onto itself, if row)
    currentRow.className = "ready-for-drop";
    // To know which row has been the last one entered when this function will
    // be called again, assign the previousRow variable of the global scope onto
    // the currentRow from this function run
    previousRow = currentRow;
  }
}

非常に基本的なコメントがコードに残されているため、このコードは初心者にも適しています。これがあなたを助けることを願っています!もちろん、これが機能するためには、前述のすべてのイベントリスナーを各テーブルセルに追加する必要があります。


0

これは、イベントのタイミングに基づく別のアプローチです。

dragenter子要素から送出されたイベントは、親要素によって捕獲することができ、それは常に前に発生しますdragleave。これらの2つのイベント間のタイミングは非常に短く、人間のマウスの動作よりも短いです。したがって、アイデアは、dragenter発生した時間を記憶し、dragleaveその後に「あまり速く」発生しないイベントをフィルタリングすることです...

この短い例は、ChromeとFirefoxで動作します。

var node = document.getElementById('someNodeId'),
    on   = function(elem, evt, fn) { elem.addEventListener(evt, fn, false) },
    time = 0;

on(node, 'dragenter', function(e) {
    e.preventDefault();
    time = (new Date).getTime();
    // Drag start
})

on(node, 'dragleave', function(e) {
    e.preventDefault();
    if ((new Date).getTime() - time > 5) {
         // Drag end
    }
})

0

pimvdb ..

dragleaveの代わりにdropを使ってみませんか。それは私のために働いた。これで問題が解決することを願っています。

jsFiddleを確認してください:http ://jsfiddle.net/HU6Mk/118/

$('#drop').bind({
                 dragenter: function() {
                     $(this).addClass('red');
                 },

                 drop: function() {
                     $(this).removeClass('red');
                 }
                });

$('#drag').bind({
                 dragstart: function(e) {
                     e.allowedEffect = "copy";
                     e.setData("text/plain", "test");
                 }
                });

2
ユーザーが気が変わってファイルをブラウザからドラッグして戻した場合、これは赤のままです。
ZachB

0

transitioningフラグ付きのタイムアウトを使用して、一番上の要素をリッスンできます。 dragenter/ dragleave子イベントからコンテナにバブルアップします。

dragenter子要素dragleaveはコンテナの前に起動するので、フラグの表示を1msの遷移として設定します。dragleaveリスナーは1msが経過する前にフラグをチェックします。

このフラグは、子要素への遷移時にのみtrueになり、(コンテナーの)親要素に遷移するときはtrueになりません

var $el = $('#drop-container'),
    transitioning = false;

$el.on('dragenter', function(e) {

  // temporarily set the transitioning flag for 1 ms
  transitioning = true;
  setTimeout(function() {
    transitioning = false;
  }, 1);

  $el.toggleClass('dragging', true);

  e.preventDefault();
  e.stopPropagation();
});

// dragleave fires immediately after dragenter, before 1ms timeout
$el.on('dragleave', function(e) {

  // check for transitioning flag to determine if were transitioning to a child element
  // if not transitioning, we are leaving the container element
  if (transitioning === false) {
    $el.toggleClass('dragging', false);
  }

  e.preventDefault();
  e.stopPropagation();
});

// to allow drop event listener to work
$el.on('dragover', function(e) {
  e.preventDefault();
  e.stopPropagation();
});

$el.on('drop', function(e) {
  alert("drop!");
});

jsfiddle:http ://jsfiddle.net/ilovett/U7mJj/


0

ドラッグターゲットのすべての子オブジェクトのポインタイベントを削除する必要があります。

function disableChildPointerEvents(targetObj) {
        var cList = parentObj.childNodes
        for (i = 0; i < cList.length; ++i) {
            try{
                cList[i].style.pointerEvents = 'none'
                if (cList[i].hasChildNodes()) disableChildPointerEvents(cList[i])
            } catch (err) {
                //
            }
        }
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.