Das Problem, das ich habe, ist, dass das dragleave
Ereignis eines Elements ausgelöst wird, wenn ein untergeordnetes Element dieses Elements bewegt wird. Wird auch dragenter
nicht ausgelöst, wenn Sie das übergeordnete Element erneut zurückfahren.
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 es tun soll, ist den Benutzer zu benachrichtigen, indem der Tropfen div
rot gezogen wird, wenn etwas dorthin gezogen wird. Dies funktioniert, aber wenn Sie in das p
Kind ziehen, wird das dragleave
gefeuert und das div
ist nicht mehr rot. Wenn Sie zum Tropfen zurückkehren, wird er div
auch nicht wieder rot. Es ist notwendig, sich vollständig aus dem Tropfen zu bewegen div
und ihn wieder hineinzuziehen, um ihn rot zu machen.
Ist es möglich zu verhindern, dass dragleave
beim Ziehen in ein untergeordnetes Element ausgelöst wird?
Update 2017: TL; DR, Suchen Sie nach CSS, pointer-events: none;
wie in der Antwort von @ HD unten beschrieben, die in modernen Browsern und IE11 funktioniert.
dragleave
es in diesem Fall immer noch ausgelöst wird.Antworten:
Sie müssen nur einen Referenzzähler behalten, ihn erhöhen, wenn Sie einen Dragenter erhalten, und verringern, wenn Sie einen Dragleave erhalten. Wenn der Zähler auf 0 steht, entfernen Sie die Klasse.
Hinweis: Setzen Sie im Drop-Ereignis den Zähler auf Null zurück und löschen Sie die hinzugefügte Klasse.
Sie können es hier ausführen
quelle
pointer-events: none
auf die Kinder. Wenn ich mit dem Ziehen beginne, füge ich eine Klasse mit dieser Eigenschaft hinzu. Wenn das Ziehen vorbei ist, entferne ich die Klasse. Funktionierte gut auf Safari und Chrome, aber nicht auf Firefox.dragenter
speichere ichevent.currentTarget
in einer neuen VariablendragEnterTarget
. SolangedragEnterTarget
festgelegt, ignoriere ich weiteredragenter
Ereignisse, da sie von Kindern stammen. Auf jedendragleave
Fall überprüfe ichdragEnterTarget === event.target
. Wenn dies falsch ist, wird das Ereignis ignoriert, da es von einem Kind ausgelöst wurde. Wenn dies wahr ist , ich zurückgesetztdragEnterTarget
zuundefined
.Ja.
Dieses CSS scheint für Chrome ausreichend zu sein.
Während der Verwendung mit Firefox sollte das #drop keine Textknoten direkt haben (andernfalls gibt es ein seltsames Problem, bei dem ein Element "sich selbst überlässt" ), daher empfehle ich, es nur mit einem Element zu belassen (z. B. ein div im Inneren verwenden) #drop um alles hinein zu legen)
Hier ist eine jsfiddle , die das ursprüngliche (gebrochene) Beispiel der Frage löst .
Ich habe auch eine vereinfachte Version erstellt, die aus dem @ Theodore Brown-Beispiel stammt, aber nur auf diesem CSS basiert.
Nicht alle Browser haben dieses CSS implementiert: http://caniuse.com/pointer-events
Wenn ich den Facebook-Quellcode sehe, kann ich ihn
pointer-events: none;
mehrmals finden, aber er wird wahrscheinlich zusammen mit anmutigen Degradations-Fallbacks verwendet. Zumindest ist es so einfach und löst das Problem für viele Umgebungen.quelle
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:
In wenigen Worten erstellen Sie eine "Maske" innerhalb der Dropzone mit geerbter Breite und Höhe und absoluter Position, die nur angezeigt wird, wenn der Dragover beginnt.
Nachdem Sie diese Maske gezeigt haben, können Sie den Trick ausführen, indem Sie die anderen Dragleave & Drop-Ereignisse anhängen.
Nach dem Verlassen oder Ablegen verstecken Sie die Maske einfach wieder.
Einfach und ohne Komplikationen.
(Hinweis: Greg Pettit-Rat - Sie müssen sicher sein, dass die Maske über der gesamten Box einschließlich des Randes schwebt.)
quelle
Es ist einige Zeit her, dass diese Frage gestellt wurde und viele Lösungen (einschließlich hässlicher Hacks) bereitgestellt werden.
Ich habe es geschafft, das gleiche Problem zu beheben, das ich kürzlich dank der Antwort in dieser Antwort hatte, und dachte, es könnte für jemanden hilfreich sein, der auf diese Seite gelangt. Die ganze Idee ist, das
evenet.target
inondrageenter
jedem Mal zu speichern, wenn es auf einem der übergeordneten oder untergeordneten Elemente aufgerufen wird. Dannondragleave
prüfen Sie, ob das aktuelle Ziel (event.target
) ist gleich das Objekt , das Sie in gespeichertondragenter
.Der einzige Fall, in dem diese beiden übereinstimmen, ist, wenn Sie das Browserfenster verlassen.
Der Grund, warum dies gut funktioniert, ist, wenn die Maus ein Element verlässt (sagen wir
el1
) und ein anderes Element betritt (sagen wirel2
), zuerst wird dasel2.ondragenter
aufgerufen und dannel1.ondragleave
. Nur wenn das Ziehen das Browserfenster verlässt / betritt,event.target
wird''
in beidenel2.ondragenter
undel1.ondragleave
.Hier ist mein Arbeitsprobe. Ich habe es auf IE9 +, Chrome, Firefox und Safari getestet.
Und hier ist eine einfache HTML-Seite:
Mit dem richtigen Styling habe ich das innere div (# file-drop-area) viel größer gemacht, wenn eine Datei auf den Bildschirm gezogen wird, damit der Benutzer die Dateien einfach an der richtigen Stelle ablegen kann.
quelle
==
Over===
. Sie vergleichen die Ereigniszielobjektreferenzen und===
funktionieren daher auch einwandfrei.Der "richtige" Weg, um dieses Problem zu lösen, besteht darin, Zeigerereignisse für untergeordnete Elemente des Ablageziels zu deaktivieren (wie in der Antwort von @ HD). Hier ist eine von mir erstellte jsFiddle, die diese Technik demonstriert . Leider funktioniert dies in Versionen von Internet Explorer vor IE11 nicht, da Zeigerereignisse nicht unterstützt wurden .
Glücklicherweise konnte ich mit einer Vermeidung des Problems kommen , die tut Arbeit in alten Versionen von IE. Grundsätzlich geht es darum,
dragleave
Ereignisse zu identifizieren und zu ignorieren , die beim Ziehen über untergeordnete Elemente auftreten. Da dasdragenter
Ereignis auf untergeordneten Knoten vor demdragleave
Ereignis auf dem übergeordneten Knoten ausgelöst wird , können jedem untergeordneten Knoten separate Ereignis-Listener hinzugefügt werden, die eine Klasse "Ignorieren-Ziehen-Verlassen" zum Ablageziel hinzufügen oder daraus entfernen. Dann kann derdragleave
Ereignis-Listener des Drop-Ziels Aufrufe, die auftreten, wenn diese Klasse vorhanden ist, einfach ignorieren. Hier ist eine jsFiddle, die diese Problemumgehung demonstriert . Es ist getestet und funktioniert in Chrome, Firefox und IE8 +.Aktualisieren:
Ich habe eine jsFiddle erstellt, die eine kombinierte Lösung mithilfe der Funktionserkennung demonstriert , bei der Zeigerereignisse verwendet werden, wenn sie unterstützt werden (derzeit Chrome, Firefox und IE11), und der Browser auf das Hinzufügen von Ereignissen zu untergeordneten Knoten zurückgreift, wenn die Unterstützung von Zeigerereignissen nicht verfügbar ist (IE8) -10).
quelle
dragleave
Ereignis in Chrome, Firefox und IE11 + ausgelöst wird. Ich habe neben IE10 auch meine andere Problemumgehung aktualisiert, um IE8 und IE9 zu unterstützen. Es ist beabsichtigt, dass der Dropzone-Effekt nur beim Ziehen des Links "Drag me" hinzugefügt wird. Andere können dieses Verhalten nach Bedarf ändern, um ihre Anwendungsfälle zu unterstützen.Das Problem ist, dass das
dragleave
Ereignis ausgelöst wird, wenn sich die Maus vor dem untergeordneten Element befindet.Ich habe verschiedene Methoden ausprobiert, um festzustellen, ob das
e.target
Element mit dem Element identischthis
ist, konnte jedoch keine Verbesserung erzielen.Die Art und Weise, wie ich dieses Problem behoben habe, war ein bisschen hacken, funktioniert aber zu 100%.
quelle
if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
e.x
und hate.y
.Wenn Sie HTML5 verwenden, können Sie den clientRect des Elternteils abrufen:
Dann in der parent.dragleave ():
Hier ist eine Geige
quelle
border-radius
hat das Bewegen des Zeigers in die Nähe der Ecke das Element tatsächlich verlassen , aber dieser Code würde immer noch denken, dass wir uns im Inneren befinden (wir haben das Element verlassen, befinden uns aber immer noch im Begrenzungsrechteck). Dann wird derdragleave
Event-Handler überhaupt nicht aufgerufen.Eine sehr einfache Lösung ist die Verwendung der
pointer-events
CSS-Eigenschaft . Setzen Sie den Wert einfachnone
beim Dragstart für jedes untergeordnete Element. Diese Elemente lösen keine mausbezogenen Ereignisse mehr aus, sodass sie nicht mehr mit der Maus darüber fahren und somit kein Dragleave auf dem übergeordneten Element auslösen .Vergessen Sie nicht, diese Eigenschaft
auto
beim Beenden des Ziehens auf zurückzusetzen;)quelle
Diese ziemlich einfache Lösung funktioniert bisher für mich, vorausgesetzt, Ihr Ereignis wird jedem Drag-Element einzeln zugeordnet.
quelle
Sehr einfache Lösung:
quelle
Sie können es in Firefox mit ein wenig Inspiration aus dem jQuery-Quellcode beheben :
Leider funktioniert es in Chrome
relatedTarget
nicht, da es beidragleave
Ereignissen anscheinend nicht vorhanden ist , und ich gehe davon aus, dass Sie in Chrome arbeiten, weil Ihr Beispiel in Firefox nicht funktioniert hat. Hier ist eine Version mit dem oben implementierten Code implementiert.quelle
Und los geht's, eine Lösung für Chrome:
quelle
border-radius
wie ich in meinem Kommentar zu der Antwort von @ azlar oben erläutert habe.Hier ist eine andere Lösung mit document.elementFromPoint:
Hoffe das funktioniert, hier ist eine Geige .
quelle
event.clientX, event.clientY
stattdessen verwenden, da sie relativ zum Ansichtsfenster und nicht zur Seite sind. Ich hoffe, das hilft einer anderen verlorenen Seele da draußen.Eine einfache Lösung besteht darin, die CSS-Regel
pointer-events: none
zur untergeordneten Komponente hinzuzufügen , um den Auslöser von zu verhindernondragleave
. Siehe Beispiel:quelle
dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
Ich bin mir nicht sicher, ob es sich um einen Cross-Browser handelt, aber ich habe ihn in Chrome getestet und er 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 ziehe. 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 ein Dokument ziehen, zeige ich es, und wenn Sie auf das übergeordnete Dokument fallen, wird es behandelt, und wenn Sie das übergeordnete Dokument verlassen, überprüfe ich x und y.
quelle
Ich habe eine kleine Bibliothek namens Dragster geschrieben , um genau dieses Problem zu lösen. Sie funktioniert überall, außer im IE stillschweigend nichts zu tun (was DOM-Ereigniskonstruktoren nicht unterstützt, aber es wäre ziemlich einfach, mit den benutzerdefinierten Ereignissen von jQuery etwas Ähnliches zu schreiben).
quelle
Ich hatte das gleiche Problem und versuchte, die pk7s-Lösung zu verwenden. Es hat funktioniert, aber es könnte ein bisschen besser ohne zusätzliche Dom-Elemente gemacht werden.
Grundsätzlich ist die Idee dieselbe - fügen Sie eine zusätzliche nicht sichtbare Überlagerung über dem ablegbaren Bereich hinzu. Lassen Sie uns dies nur ohne zusätzliche dom-Elemente tun. Hier ist die Rolle, in der CSS-Pseudoelemente zum Einsatz kommen.
Javascript
CSS
Diese Nachregel erstellt eine vollständig abgedeckte Überlagerung für den ablegbaren Bereich.
Hier ist die vollständige Lösung: http://jsfiddle.net/F6GDq/8/
Ich hoffe es hilft jedem mit dem gleichen Problem.
quelle
Überprüfen Sie einfach, ob das gezogene Element ein untergeordnetes Element ist. Wenn dies der Fall ist, entfernen Sie nicht Ihre Stilklasse "Dragover". Ziemlich einfach und funktioniert bei mir:
quelle
Ich bin auf dasselbe Problem gestoßen und hier ist meine Lösung - die meiner Meinung nach viel einfacher ist als oben. Ich bin mir nicht sicher, ob es sich um einen Crossbrowser handelt (hängt möglicherweise von der Reihenfolge des Sprudelns ab).
Ich werde der Einfachheit halber jQuery verwenden, aber die Lösung sollte rahmenunabhängig sein.
Das Ereignis sprudelt so oder so zu den Eltern:
Wir fügen Ereignisse bei
Und das war's auch schon. :) es funktioniert, weil, obwohl onEnter on child vor onLeave on parent ausgelöst wird, die Reihenfolge etwas verzögert wird, sodass die Klasse zuerst entfernt und nach einer Millisekunde erneut angewendet wird.
quelle
Ich habe ein Drag-and-Drop-Modul namens Drip-Drop geschrieben, das unter anderem dieses verrückte Verhalten behebt. Wenn Sie nach einem guten einfachen Drag-and-Drop-Modul suchen, das Sie als Grundlage für alles verwenden können (Datei-Upload, In-App-Drag-and-Drop, Ziehen von oder zu externen Quellen), sollten Sie dies überprüfen Modul aus:
https://github.com/fresheneesz/drip-drop
So würden Sie das tun, was Sie in Drip-Drop versuchen:
Um dies ohne Bibliothek zu tun, habe ich die Zählertechnik in Drip-Drop verwendet, wobei die am höchsten bewertete Antwort wichtige Schritte übersieht, die dazu führen, dass die Dinge für alles außer dem ersten Tropfen kaputt gehen. So geht's richtig:
quelle
Eine alternative Arbeitslösung, etwas einfacher.
quelle
clientX
über 0 erhalten, wenn sich die Maus außerhalb der Box befindet. Zugegeben, meine Elemente sindposition:absolute
Nachdem ich so viele Stunden verbracht hatte, bekam ich diesen Vorschlag, genau wie beabsichtigt zu arbeiten. Ich wollte nur dann einen Hinweis geben, wenn Dateien über das Drag & Drop-Dokument gezogen wurden und Dragleave im Chrome-Browser schmerzhaftes Flimmern verursachte.
Auf diese Weise habe ich es gelöst und auch die richtigen Hinweise für den Benutzer eingegeben.
quelle
Ich hatte ein ähnliches Problem: Mein Code zum Ausblenden der Dropzone beim Dragleave-Ereignis für den Körper wurde beim Schweben untergeordneter Elemente, die die Dropzone in Google Chrome flackern ließen, kontingent ausgelöst.
Ich konnte dies lösen, indem ich die Funktion zum Ausblenden der Dropzone plante, anstatt sie sofort aufzurufen. Wenn dann ein anderer Dragover oder Dragleave ausgelöst wird, wird der geplante Funktionsaufruf abgebrochen.
quelle
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 die Eltern und nicht die Nachkommen fallen gelassen werden können . Ich denke event.stopPropogation () hätte diesen Fall behandeln sollen, aber es scheint, als würde es nicht den Trick machen.
Die oben genannten Lösungen scheinen in den meisten Fällen zu funktionieren, schlagen jedoch bei den untergeordneten Elementen fehl , die 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. Ignorieren Sie dann das Dragleave- Ereignis, wie ich es hier getan habe:
Sie können eine Arbeits Geige finden hier !
quelle
Gelöst ..!
Deklarieren Sie ein beliebiges Array, zum Beispiel:
Sie müssen sich keine Sorgen um untergeordnete Elemente machen.
quelle
Ich hatte viel damit zu kämpfen, selbst nachdem ich all diese Antworten durchgelesen hatte, und dachte, ich könnte meine Lösung mit Ihnen teilen, weil ich dachte, es könnte einer der einfacheren Ansätze sein, allerdings etwas anders. Mein Gedanke war, den
dragleave
Ereignis-Listener einfach komplett wegzulassen und das Dragleave-Verhalten bei jedem neuen ausgelösten Dragenter-Ereignis zu codieren, während sichergestellt wurde, dass Dragenter-Ereignisse nicht unnötig ausgelöst werden.In meinem Beispiel unten habe ich eine Tabelle, in der ich den Inhalt von Tabellenzeilen über die Drag & Drop-API miteinander austauschen möchte. Ein
dragenter
, dem Zeilenelement, in das Sie gerade Ihr Element ziehen, wird eine CSS-Klasse hinzugefügt, um es hervorzuheben, unddragleave
diese Klasse wird entfernt.Beispiel:
Sehr einfache HTML-Tabelle:
Und die dragenter- Event - Handler - Funktion, hinzugefügt auf jede Tabellenzelle (beiseite
dragstart
,dragover
,drop
, unddragend
Handler, die auf diese Frage nicht spezifisch sind, so hier nicht kopiert):Sehr einfache Kommentare im Code, so dass dieser Code auch für Anfänger geeignet ist. Hoffe das wird dir helfen! Beachten Sie, dass Sie natürlich alle oben erwähnten Ereignis-Listener zu jeder Tabellenzelle hinzufügen müssen, damit dies funktioniert.
quelle
Hier ist ein anderer Ansatz, der auf dem Timing von Ereignissen basiert.
Das
dragenter
vom untergeordneten Element ausgelöste Ereignis kann vom übergeordneten Element erfasst werden und tritt immer vor dem aufdragleave
. Das Timing zwischen diesen beiden Ereignissen ist sehr kurz und kürzer als jede mögliche menschliche Mausaktion. Die Idee ist also, sich die Zeit zu merken, zu der ein Ereignis eintrittdragenter
, unddragleave
Ereignisse zu filtern , die "nicht zu schnell" nach ...Dieses kurze Beispiel funktioniert unter Chrome und Firefox:
quelle
pimvdb ..
Warum Sie versuchen , nicht mit Tropfen statt Dragleave . Es hat bei mir funktioniert. Ich hoffe, dies löst Ihr Problem.
Bitte überprüfen Sie die jsFiddle: http://jsfiddle.net/HU6Mk/118/
quelle
Sie können eine Zeitüberschreitung mit einem
transitioning
Flag verwenden und das oberste Element abhören.dragenter
/dragleave
von untergeordneten Ereignissen sprudelt in den Container.Da
dragenter
das untergeordnete Element vordragleave
dem Container ausgelöst wird, setzen wir das Flag show als Übergang für 1 ms. Derdragleave
Listener sucht nach dem Flag, bevor 1 ms aktiv ist.Das Flag ist nur bei Übergängen zu untergeordneten Elementen wahr und beim Übergang zu einem übergeordneten Element (des Containers) nicht wahr.
jsfiddle: http://jsfiddle.net/ilovett/U7mJj/
quelle
Sie müssen die Zeigerereignisse für alle untergeordneten Objekte des Ziehziels entfernen.
quelle