wake-up-neo.net

HTML5-Dragleave wird ausgelöst, wenn ein untergeordnetes Element angezeigt wird

Das Problem, das ich habe, ist, dass das dragleave -Ereignis eines Elements ausgelöst wird, wenn ein untergeordnetes Element dieses Elements angezeigt wird. Außerdem wird dragenter nicht ausgelöst, wenn Sie das übergeordnete Element erneut in den Hintergrund setzen.

Ich habe eine vereinfachte Geige gemacht: 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>

mit folgendem 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");
                 }
                });

Was er tun soll, ist den Benutzer zu benachrichtigen, indem er die Variable div rot macht, wenn etwas dort gezogen wird. Dies funktioniert, aber wenn Sie in das Kind p ziehen, wird die dragleave ausgelöst und die div ist nicht mehr rot. Wenn Sie zum Drop div zurückkehren, wird er auch nicht wieder rot. Sie müssen sich vollständig aus dem Drop div herausbewegen und erneut in den Drop ziehen, um ihn rot zu machen.

Kann verhindert werden, dass dragleave beim Ziehen in ein untergeordnetes Element ausgelöst wird?

2017 Update: TL; DR, CSS pointer-events: none; nachschlagen, wie in der Antwort von @HD beschrieben. Diese Funktion funktioniert in modernen Browsern und IE11.

238
pimvdb

Sie brauchen nur einen Referenzzähler zu behalten, ihn zu erhöhen, wenn Sie ein Dragenter erhalten. Wenn der Zähler auf 0 steht - entfernen Sie die Klasse.

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');
        }
    }
});

Hinweis: Setzen Sie den Zähler im Drop-Ereignis auf Null zurück und löschen Sie die hinzugefügte Klasse.

Sie können es ausführen hier

278
Woody

Kann verhindert werden, dass Dragleave beim Ziehen in ein untergeordnetes Element ausgelöst wird?

Ja.

#drop * {pointer-events: none;}

Das CSS scheint für Chrome genug zu sein.

Bei der Verwendung mit Firefox sollte das #drop keine direkten Textknoten haben (ansonsten gibt es ein seltsames Problem, bei dem ein Element "sich selbst überlassen" ). Ich schlage daher vor, es nur mit einem Element zu belassen (z. B. Verwenden Sie ein div innerhalb von #drop, um alles hineinzulegen.

Hier ist ein jsfiddle Lösen der ursprünglichen Frage (gebrochen) Beispiel .

Ich habe auch eine vereinfachte Version aus dem @ Theodore Brown-Beispiel erstellt, die jedoch nur in diesem CSS basiert.

Nicht alle Browser haben jedoch dieses CSS implementiert: http://caniuse.com/pointer-events

Den Facebook-Quellcode zu sehen, konnte ich pointer-events: none; mehrmals finden, obwohl er wahrscheinlich zusammen mit würdevollen Degradations-Fallbacks verwendet wird. Zumindest ist es so einfach und löst das Problem für viele Umgebungen.

62
H.D.

Hier die einfachste Cross-Browser-Lösung (im Ernst):

jsfiddle <- Versuchen Sie, eine Datei in die Box zu ziehen

Sie können so etwas tun:

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);

Mit wenigen Worten, Sie erstellen eine "Maske" in der Dropzone, deren Breite und Höhe (absolute Position) geerbt wird. Diese wird nur angezeigt, wenn der Dragover beginnt.
Nachdem Sie diese Maske gezeigt haben, können Sie den Trick ausführen, indem Sie die anderen Dragleave & Drop-Ereignisse darauf anhängen.

Nach dem Verlassen oder Ablegen blenden Sie die Maske wieder aus. 
Einfach und ohne Komplikationen.

(Anmerkung: Greg Pettit Ratschlag - Sie müssen sicher sein, dass die Maske die gesamte Box einschließlich der Umrandung zeigt)

38

Die "richtige" Möglichkeit, dieses Problem zu lösen, besteht darin, Zeigerereignisse für untergeordnete Elemente des Ablageziels zu deaktivieren (wie in der Antwort von @ H.D.). Hier ist eine von mir erstellte jsFiddle, die diese Technik demonstriert . Leider funktioniert dies in Versionen von Internet Explorer vor IE11 nicht, da diese Zeigerereignisse wurden nicht unterstützt .

Zum Glück konnte ich eine Problemumgehung finden, die in alten IE-Versionen funktioniert. Grundsätzlich geht es darum, dragleave Ereignisse zu identifizieren und zu ignorieren, die beim Ziehen über untergeordnete Elemente auftreten. Da das dragenter -Ereignis auf untergeordneten Knoten vor dem dragleave -Ereignis auf dem übergeordneten Knoten ausgelöst wird, können jedem untergeordneten Knoten separate Ereignis-Listener hinzugefügt werden, die eine "Ignore-Drag-Leave" -Klasse hinzufügen oder daraus entfernen Ziel fallen lassen. Dann kann der Ereignis-Listener dragleave des Ablageziels einfach Aufrufe ignorieren, die auftreten, wenn diese Klasse existiert. Hier ist ein jsFiddle demonstriert diese Problemumgehung . Es ist getestet und funktioniert in Chrome, Firefox und IE8 +.

Update:

Ich habe ein jsFiddle, das eine kombinierte Lösung demonstriert mithilfe der Feature-Erkennung erstellt, bei der Zeigerereignisse verwendet werden, sofern dies unterstützt wird (derzeit Chrome, Firefox und IE11), und der Browser greift auf das Hinzufügen von Ereignissen zu untergeordneten Knoten zurück, wenn Zeigerereignisse auftreten Unterstützung ist nicht verfügbar (IE8-10).

34
Theodore Brown

Es wurde einige Zeit vergangen, nachdem diese Frage gestellt wurde, und es wurden viele Lösungen (einschließlich hässlicher Hacks) bereitgestellt. 

Ich habe es geschafft, das gleiche Problem zu lösen, das ich kürzlich hatte, dank der Antwort in dieser answer und dachte, es könnte für jemanden hilfreich sein, der zu dieser Seite durchkommt .. Die ganze Idee ist, die evenet.target in ondrageenter bei jedem Aufruf eines der übergeordneten oder untergeordneten Elemente. Prüfen Sie dann in ondragleave, ob das aktuelle Ziel (event.target) dem in ondragenter gespeicherten Objekt entspricht.

Der einzige Fall, dass diese beiden übereinstimmen, ist der Fall, wenn Sie das Browserfenster durch Ziehen verlassen.

Das funktioniert gut, wenn die Maus ein Element verlässt (sagen Sie el1) und ein anderes Element eingeben (sagen Sie el2). Zuerst wird der el1.ondragenter aufgerufen und dann el2.ondragleave. Nur wenn das Ziehen das Browserfenster verlässt/betritt, wird event.target in '' und el1.ondragenterel2.ondragleave angezeigt.

Hier ist meine Arbeitsprobe. Ich habe es auf IE9 +, Chrome, Firefox und Safari getestet.

(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;
    };
})();

Und hier ist eine einfache HTML-Seite:

<!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>

Mit dem richtigen Styling habe ich das innere div (# file-drop-area) immer größer gemacht, wenn eine Datei in den Bildschirm gezogen wird, sodass der Benutzer die Dateien problemlos an den richtigen Ort ziehen kann.

25
Ali Motevallian

Das Problem ist, dass das Ereignis dragleave ausgelöst wird, wenn sich die Maus vor dem untergeordneten Element befindet.

Ich habe verschiedene Überprüfungsmethoden ausprobiert, um festzustellen, ob das e.target-Element mit dem this-Element identisch ist, aber keine Verbesserung erzielen konnte.

Die Art und Weise, wie ich dieses Problem gelöst habe, war ein bisschen hackend, funktioniert aber zu 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');
               }
           }
10
Greg

Eine sehr einfache Lösung ist die Verwendung der CSS-Eigenschaft pointer-events . Setzen Sie den Wert bei dragstart für jedes untergeordnete Element auf none. Diese Elemente lösen keine mausbezogenen Ereignisse mehr aus, sodass sie nicht mit der Maus darüber gefangen werden und somit nicht dragleave auf dem übergeordneten Element ausgelöst werden. 

Vergessen Sie nicht, diese Eigenschaft beim Beenden des Ziehens wieder auf auto zu setzen;)

8
FruityFred

Sie können es in Firefox mit ein wenig Inspiration aus dem jQuery-Quellcode beheben :

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

    if (related !== this) {

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

        if (!inside) {

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

}

Leider funktioniert es in Chrome nicht, weil relatedTarget bei dragleave-Ereignissen nicht vorhanden zu sein scheint, und ich gehe davon aus, dass Sie in Chrome arbeiten, weil Ihr Beispiel in Firefox nicht funktioniert hat. Hier ist eine Version mit dem obigen Code implementiert.

8
robertc

Und hier geht es, eine Lösung für 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!');
                    }
                })
7
Aldekein

Hier ist eine andere Lösung, die document.elementFromPoint verwendet:

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

Hoffe das klappt, hier ist eine Geige .

6
marcelj

Ich habe eine kleine Bibliothek mit dem Namen Dragster geschrieben, um genau dieses Problem zu behandeln. Es funktioniert überall, außer im stillen Nichts in IE (was DOM Event Constructors nicht unterstützt, aber es wäre ziemlich einfach zu schreiben etwas ähnliches mit den benutzerdefinierten Ereignissen von jQuery)

5
Ben

Ich bin mir nicht sicher, ob es sich um einen Cross-Browser handelt, aber ich habe es in Chrome getestet und es löst mein Problem:

Ich möchte eine Datei über die gesamte Seite ziehen und ablegen, aber mein DragLeave wird ausgelöst, wenn ich über ein untergeordnetes Element gezogen werde. Meine Lösung bestand darin, das x und y der Maus zu betrachten:

ich habe ein Div, das meine gesamte Seite überlagert. Wenn die Seite geladen wird, verstecke ich sie.

wenn Sie über das Dokument ziehen, zeige ich es, und wenn Sie es auf dem übergeordneten Objekt ablegen, behandelt es es, und wenn Sie es verlassen, überprüfen Sie x und 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;
});
4
chrisallick

Ich hatte das gleiche Problem und versuchte die pk7s-Lösung zu verwenden. Es hat funktioniert, aber es könnte etwas besser gemacht werden, ohne zusätzliche Dom-Elemente.

Grundsätzlich ist die Idee gleich - fügen Sie eine zusätzliche unsichtbare Überlagerung über dem abwerfbaren Bereich hinzu. Lässt dies nur ohne zusätzliche dom-Elemente tun. Hier ist der Teil, an dem CSS-Pseudo-Elemente zum Einsatz kamen.

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

Mit dieser Regel wird eine vollständig abgedeckte Fläche für abwischbaren Bereich erstellt.

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

Hier ist die vollständige Lösung: http://jsfiddle.net/F6GDq/8/

Ich hoffe es hilft jedem mit dem gleichen Problem.

4
rasmusx

Eine einfache Lösung ist das Hinzufügen der css-Regel pointer-events: none zur untergeordneten Komponente, um den Auslöser von ondragleave zu verhindern. Siehe Beispiel:

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>

4
Alexandre Annic

Ich bin auf dasselbe Problem gestoßen und hier ist meine Lösung - die meiner Meinung nach viel einfacher ist als oben. Ich bin nicht sicher, ob es sich um einen Crossbrowser handelt (kann sogar von einer blubbernden Reihenfolge abhängen)

Ich werde jQuery zur Vereinfachung verwenden, aber die Lösung sollte vom Framework unabhängig sein.

Das Ereignis sprudelt zu beiden übergeordneten Elementen:

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

Wir hängen Ereignisse an

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

Und das ist es auch schon. :) es funktioniert, auch wenn onEnter bei child feuert, bevor onLeave bei parent abläuft, verzögern wir die Reihenfolge etwas, so dass die Klasse zuerst entfernt wird und erst nach einer Milisekunde erneut verwendet wird.

3

Prüfen Sie einfach, ob es sich bei dem überzogenen Element um ein untergeordnetes Element handelt, und entfernen Sie dann nicht die Stilklasse "Ziehen". Ziemlich einfach und funktioniert für mich: 

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

wenn Sie HTML5 verwenden, können Sie clientRect des übergeordneten Objekts erhalten:

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

Dann in der parent.dragleave ():

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

hier ist ein jsfiddle

3
azlar

Eine alternative Arbeitslösung, etwas einfacher.

//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) {
2
Profet

Nachdem ich so viele Stunden damit verbracht hatte, bekam ich diesen Vorschlag genau wie beabsichtigt. Ich wollte nur einen Hinweis geben, wenn Dateien gezogen wurden, und das Ziehen von Dokumenten, Dragleave, verursachte in Chrome-Browser schmerzhafte Flimmer.

So habe ich es gelöst und auch die richtigen Hinweise für den Benutzer gegeben.

$(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
});
2
visitsb

Ich habe ein Drag-and-Drop-Modul namens drip-drop geschrieben, das dieses verrückte Verhalten unter anderem behebt. Wenn Sie nach einem guten Low-Level-Drag & Drop-Modul suchen, das Sie als Grundlage für alles verwenden können (Datei-Upload, Drag & Drop in der App, Ziehen von oder zu externen Quellen), sollten Sie dies überprüfen Modul heraus:

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

So würden Sie das tun, was Sie in Drip-Drop versuchen wollen:

$('#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')
    }
  })
})

Um dies ohne eine Bibliothek zu tun, ist die Counter-Technik genau das, was ich in Drip-Drop verwendet habe. Die am besten bewertete Antwort lässt wichtige Schritte aus, bei denen alles außer dem ersten Drop kaputt geht. So machen Sie es richtig:

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
B T

Das Ereignis "dragleave" wird ausgelöst, wenn der Mauszeiger den Ziehbereich des Zielcontainers verlässt.

Das ist sehr sinnvoll, da in vielen Fällen nur das übergeordnete Element droppable sein kann und nicht die Nachkommen .. _. Ich denke, event.stopPropogation () sollte diesen Fall behandelt haben, aber es scheint, als würde dies nicht der Fall sein. tue den Trick.

Einige der oben genannten Lösungen scheinen in den meisten Fällen zu funktionieren, schlagen jedoch fehl, wenn diese Kinder dragenter/dragleave -Ereignisse wie iframe nicht unterstützen. 

Eine Problemumgehung besteht darin, das event.relatedTarget zu überprüfen und zu überprüfen, ob es sich im Container befindet. Dann ignorieren Sie das dragleave -Ereignis wie hier:

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");
});

Sie finden eine funktionierende Geige hier !

1
Lalit Nankani

Ich hatte ein ähnliches Problem: Mein Code für das Ausblenden der Dropzone bei Dragleave-Ereignissen für den Körper wurde kontaminiert ausgelöst, wenn Kinderelemente mit der Maus über die Drop-Zone gelegt wurden, die die Dropzone in Google Chrome zum Flimmern bringen.

Ich konnte dieses Problem lösen, indem ich die Funktion zum Verbergen von Dropzone einplante, anstatt sie sofort aufzurufen. Wenn ein anderer Dragover oder Dragleave ausgelöst wird, wird der geplante Funktionsaufruf abgebrochen.

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);
1
Arseny

Sie können ein Timeout mit einem transitioning-Flag verwenden und das oberste Element abhören. dragenter/dragleave von untergeordneten Ereignissen sprudeln in den Container.

Da dragenter für das untergeordnete Element vor dragleave des Containers ausgelöst wird, wird die Flag-Show für 1 ms als Übergang gesetzt ... Der dragleave-Listener prüft die Flag, bevor 1ms ist oben.

Das Flag ist nur während der Übergänge zu untergeordneten Elementen wahr und beim Übergang zu einem übergeordneten Element (des Containers) nicht wahr.

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
ilovett

verwenden Sie diesen Code http://jsfiddle.net/HU6Mk/258/ :

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

         dragleave: function(event) {
             var x = event.clientX, y = event.clientY,
                 elementMouseIsOver = document.elementFromPoint(x, y);
             if(!$(elementMouseIsOver).closest('.red').length) {
                 $(this).removeClass('red');
             }
        }
    });
0
Mohammad Rezaei

Hier ist ein weiterer Ansatz, der auf dem Zeitpunkt von Ereignissen basiert.

Das vom untergeordneten Element ausgehende dragenter-Ereignis kann vom übergeordneten Element erfasst werden und tritt immer vor der dragleave auf. Das Timing zwischen diesen beiden Ereignissen ist wirklich kurz und kürzer als jede mögliche menschliche Mausaktion. Die Idee ist, sich die Zeit zu merken, zu der eine dragenter auftritt, und dragleave-Ereignisse zu filtern, die "nicht zu schnell" nach ... auftreten.

Dieses kurze Beispiel funktioniert in Chrome und 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
smrtl

Sie müssen die Zeigerereignisse für alle untergeordneten Objekte des Ziehziels entfernen.

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) {
                //
            }
        }
    }
0
EddieB

Versuchen Sie einfach, die event.eventPhase zu verwenden. Es wird nur dann auf 2 (Event.AT_TARGET) gesetzt, wenn das Ziel eingegeben wird. Andernfalls wird es auf 3 (Event.BUBBLING_PHASE) gesetzt.

Die EventPhase wurde verwendet, um das Dragleave-Ereignis zu binden oder dessen Bindung aufzuheben.

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

  if(e.eventPhase === Event.AT_TARGET) {

    $('.dropzone').addClass('drag-over');

    $('.dropzone').on('dragleave', function(e) {
      $('.dropzone').removeClass('drag-over');
    });

  }else{

    $('.dropzone').off('dragleave');

  }
})

Guido

0
GNaruhn

Ich habe eine ähnliche, aber elegantere Lösung für die Antwort von @ azlar gefunden, und dies ist meine Lösung:

$(document).on({
    dragenter: function(e) {
        e.stopPropagation();
        e.preventDefault();
        $("#dragging").show();
    },
    dragover: function(e) {
        e.stopPropagation();
        e.preventDefault();
    },
    dragleave: function(e) {
        e.stopPropagation();
        e.preventDefault();
        if (e.clientX <= 0 ||
            // compare clientX with the width of browser viewport
            e.clientX >= $(window).width() ||
            e.clientY <= 0 ||
            e.clientY >= $(window).height())
            $("#dragging").hide();
    }
});

Diese Methode erkennt, ob die Maus die Seite verlassen hat, und funktioniert in Chrome und Edge gut.

0
zysite

Diese recht einfache Lösung funktioniert bisher für mich, vorausgesetzt, Ihr Ereignis ist jedem Drag-Element einzeln zugeordnet. 

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

Sehr einfache Lösung:

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

pimvdb ..

Warum probierst du es nicht mit drop anstelle von dragleave aus. Es hat für mich funktioniert. hoffe das löst dein Problem. 

Bitte überprüfen Sie die 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");
                 }
                });
0
abhi