Überblick
Ich habe die folgende HTML-Struktur und habe die dragenter
und dragleave
-Ereignisse an das <div id="dropzone">
Element angehängt .
<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>
Problem
Wenn ich eine Datei über das ziehe <div id="dropzone">
, wird das dragenter
Ereignis wie erwartet ausgelöst. Wenn ich jedoch mit der Maus über ein untergeordnetes Element bewege, z. B. <div id="drag-n-drop">
, wird das dragenter
Ereignis für das <div id="drag-n-drop">
Element und dann das dragleave
Ereignis für das <div id="dropzone">
Element ausgelöst .
Wenn ich den Mauszeiger erneut über das <div id="dropzone">
Element bewege, wird das dragenter
Ereignis erneut ausgelöst, was cool ist, aber dann wird das dragleave
Ereignis für das gerade verbleibende untergeordnete Element ausgelöst, sodass die removeClass
Anweisung ausgeführt wird, die nicht cool ist.
Dieses Verhalten ist aus zwei Gründen problematisch:
Ich hänge nur
dragenter
& andragleave
an die<div id="dropzone">
so verstehe ich nicht , warum die Kinder Elemente , die diese Ereignisse angebracht , wie auch.Ich ziehe immer noch über das
<div id="dropzone">
Element, während ich über seinen Kindern schwebe, damit ich nichtdragleave
schießen will !
jsFiddle
Hier ist eine jsFiddle zum Basteln: http://jsfiddle.net/yYF3S/2/
Frage
Also ... wie kann ich es so machen, dass beim Ziehen einer Datei über das <div id="dropzone">
Element dragleave
nicht ausgelöst wird, selbst wenn ich über untergeordnete Elemente ziehe ... es sollte nur ausgelöst werden, wenn ich das <div id="dropzone">
Element verlasse . Das Schweben / Ziehen irgendwo innerhalb der Grenzen des Elements sollte das dragleave
Ereignis nicht auslösen .
Ich brauche dies, um browserübergreifend kompatibel zu sein, zumindest in den Browsern, die HTML5 Drag & Drop unterstützen, daher ist diese Antwort nicht ausreichend.
Es scheint, als hätten Google und Dropbox dies herausgefunden, aber ihr Quellcode ist minimiert / komplex, so dass ich dies aus ihrer Implementierung nicht herausfinden konnte.
e.stopPropagation();
Antworten:
Wenn Sie keine Ereignisse an die untergeordneten Elemente binden müssen, können Sie immer die Eigenschaft pointer-events verwenden.
quelle
:hover
Stile oder Klickereignishandler mehr haben ). Für den Fall, dass Sie diese Ereignisse beibehalten möchten, ist hier eine andere Problemumgehung, die ich verwendet habe: bensmithett.github.io/dragster.dropzone * {pointer-events: none;}
$('.element').addClass('dragging-over')
zu dem Element, über das$('.element').removeClass('dragging-over')
Sie ziehen , und wenn Sie es herausziehen. Dann können Sie in Ihrem CSS haben.element.dragging-over * { pointer-events: none; }
. Dadurch werden Zeigerereignisse nur beim Ziehen von allen untergeordneten Elementen entfernt.Ich habe endlich eine Lösung gefunden, mit der ich zufrieden bin. Ich habe tatsächlich mehrere Möglichkeiten gefunden, um das zu tun, was ich will, aber keine war so erfolgreich wie die aktuelle Lösung ... in einer Lösung trat häufig Flackern auf, weil dem
#dropzone
Element ein Rand hinzugefügt / entfernt wurde ... in einer anderen, der Grenze wurde nie entfernt, wenn Sie sich vom Browser entfernen.Wie auch immer, meine beste Hacky-Lösung ist folgende:
Dies funktioniert ziemlich gut, aber in Firefox sind Probleme aufgetreten, da Firefox doppelt aufgerufen wurde
dragenter
und mein Zähler ausgeschaltet war. Trotzdem ist es keine sehr elegante Lösung.Dann bin ich auf folgende Frage gestoßen: Wie erkennt man das Dragleave-Ereignis in Firefox beim Ziehen aus dem Fenster?
Also nahm ich die Antwort und wandte sie auf meine Situation an:
Dies ist sauber und elegant und scheint in jedem Browser zu funktionieren, den ich bisher getestet habe (ich habe den IE noch nicht getestet).
quelle
stopPropagation()
undpreventDefault()
wird bevorzugt. Ich würde das fallen lassenreturn false
.Das ist ein bisschen hässlich, aber es funktioniert verdammt! ...
Speichern Sie in Ihrem 'Dragenter'-Handler das event.target (in einer Variablen in Ihrem Closure oder was auch immer), und in Ihrem' Dragleave'-Handler wird Ihr Code nur ausgelöst, wenn event.target === das ist, das Sie gespeichert haben.
Wenn Ihr 'Dragenter' ausgelöst wird, wenn Sie dies nicht möchten (dh wenn er nach dem Verlassen untergeordneter Elemente eingegeben wird), befindet er sich beim letzten Auslösen, bevor die Maus das übergeordnete Element verlässt, auf dem übergeordneten Element, sodass das übergeordnete Element immer aktiviert ist der letzte 'Dragenter' vor dem beabsichtigten 'Dragleave'.
quelle
Zuerst stimmte ich den Leuten zu, die den
pointer-events: none
Ansatz verwarfen . Aber dann habe ich mich gefragt:In meinem Fall ist in den Kindern viel los, z. B. Bewegen Sie den Mauszeiger, um Schaltflächen für zusätzliche Aktionen, Inline-Bearbeitung usw. anzuzeigen. Allerdings keine davon ist jedoch während eines Ziehens erforderlich oder sogar erwünscht .
In meinem Fall verwende ich so etwas, um Zeigerereignisse für alle untergeordneten Knoten des übergeordneten Containers selektiv auszuschalten:
Verwenden Sie Ihren bevorzugten Ansatz, um die Klasse
dragging-in-progress
in dendragEnter
/dragLeave
event-Handlern hinzuzufügen / zu entfernen , wie ich es indragStart
et. al.quelle
dragstart
Ereignis ein und entfernen Sie sie indragend
.Dies scheint ein Chrome-Fehler zu sein.
Die einzige Problemumgehung, die mir in den Sinn kam, war die Erstellung eines transparenten Überlagerungselements zur Erfassung Ihrer Ereignisse: http://jsfiddle.net/yYF3S/10/
JS :
HTML :
quelle
Das Problem ist, dass Ihre Elemente in den Dropzones natürlich Teil der Dropzone sind und wenn Sie die Kinder betreten, verlassen Sie die Eltern. Dies zu lösen ist nicht einfach. Sie können auch versuchen, den Kindern Ereignisse hinzuzufügen und Ihre Klasse erneut den Eltern hinzuzufügen.
Ihre Ereignisse werden immer noch mehrmals ausgelöst, aber niemand wird sie sehen.
// Bearbeiten: Verwenden Sie das Dragmove-Ereignis, um das Dragleave-Ereignis dauerhaft zu überschreiben:
Definieren Sie das Dragleave-Ereignis nur für die Dropzone.
quelle
dragleave
... wenn ich ein Kind verlasse, bin ich vielleicht immer noch bei den Eltern,#dropzone
aber das wird dasdragenter
Ereignis nicht wieder auslösen :(dragleave
Ereignis für das gerade verlassene untergeordnete Element ausgelöst, sodass dieremoveClass
Anweisung erneut ausgeführt wirddragleave
nur für die definieren#dropzone
... wenn ich könnte, hätte ich dieses Problem nicht.Wie in dieser Antwort erwähnt , können Sie verhindern, dass untergeordnete Knoten auf Ereignisse ausgelöst werden. Wenn Sie jedoch einige Ereignisse binden müssen, gehen Sie folgendermaßen vor:
Und fügen Sie diesen Ihrem JS-Code hinzu:
quelle
Wenn Sie jQuery verwenden, überprüfen Sie dies: https://github.com/dancork/jquery.event.dragout
Es ist wirklich großartig.
EDIT: Eigentlich glaube ich nicht, dass es von jQuery abhängt, Sie können den Code wahrscheinlich auch ohne ihn verwenden.
quelle
dragleave
untergeordneten Elementen funktioniert hat , obwohl ich die übergeordnete Dropzone nicht verlassen habe.dragout
ich die letzte Stunde mit diesem Problem gekämpft hatte, war es die einzige Lösung, die für mich funktioniert hat.@hristo Ich habe eine viel elegantere Lösung. Überprüfen Sie, ob Sie dies verwenden können.
Ihre Mühe wurde schließlich nicht verschwendet. Ich habe es zuerst geschafft, deine zu verwenden, hatte aber verschiedene Probleme in FF, Chrome. Nachdem ich so viele Stunden verbracht hatte, bekam ich diesen Vorschlag, genau wie beabsichtigt zu arbeiten.
So ist die Implementierung. Ich nutzte auch visuelle Hinweise, um Benutzer korrekt über die Drop-Zone zu führen.
quelle
var dropZoneHideDelay=70, dropZoneVisible=true;
dropZoneHideDelay, dropZoneVisible
werden für zwei'on'
Dokumentereignisse in meiner Implementierung benötigt.Meine zwei Cent: Verstecke eine Ebene über deiner Dropzone und zeige sie dann, wenn du ziehst, und ziele auf die Dragleave darauf.
Demo: https://jsfiddle.net/t6q4shat/
HTML
CSS
JS
quelle
Für mich hat der Ansatz
pointer-events: none;
also nicht so gut funktioniert ... Hier ist meine alternative Lösung:Auf diese Weise ist es
dragleave
dem übergeordnetendragover
Element ( einem untergeordneten Element ) oder einem untergeordneten Element nicht möglich . hoffe das hilft :)* Die '.active'-Klasse, die ich hinzufüge, wenn ich
dragenter
oderdragleave
. Aber wenn Sie ohne das arbeiten, lassen Sie die Klasse einfach weg.quelle
Super einfache schnelle Lösung dafür, nicht ausgiebig getestet, funktioniert aber gerade in Chrome.
Entschuldigen Sie das Coffeescript.
Dies behebt den Chrome-Flimmerfehler oder zumindest die Permutation, die mir Probleme bereitete.
quelle
Diese Antwort finden Sie hier:
HTML5-Dragleave wird ausgelöst, wenn Sie den Mauszeiger über ein untergeordnetes Element bewegen
quelle
Meine Version:
Mit diesem Layout:
Ich habe einen Dump von Elementklassen für
e.target
,e.currentTarget
,e.relatedTarget
für beidedragover
unddragleave
Veranstaltungen.Es hat mir gezeigt, dass beim Verlassen des übergeordneten Blocks (
.dropzone
)e.relatedTarget
kein Kind dieses Blocks ist, sodass ich weiß, dass ich mich außerhalb der Dropzone befinde.quelle
Hier eine der einfachsten Lösungen ● ︿ ●
Schauen Sie sich diese Geige an <- versuchen Sie, eine Datei in die Box zu ziehen
Sie können so etwas tun:
Damit sollten Sie bereits wissen, was hier passiert.
Schau dir nur die Geige an, weißt du?
quelle
Ich hatte ein ähnliches Problem und habe es folgendermaßen behoben:
Das Problem: Die Funktion drop (ev) wird ausgelöst, wenn der Benutzer ein Element in der "Drop-Zone" (ul-Element) ablegt, aber leider auch, wenn das Element in einem seiner untergeordneten Elemente (li-Elemente) abgelegt wird.
Die Reparatur:
quelle
Ich habe versucht, dies selbst für ein Datei-Upload-Feld zu implementieren, bei dem sich die Feldfarbe ändert, wenn Benutzer eine Datei in den Bereich ziehen.
Ich habe eine Lösung gefunden, die eine schöne Mischung aus Javascript und CSS ist. Angenommen, Sie haben eine Drop-Zone
div
mit ID#drop
. Fügen Sie dies Ihrem Javascript hinzu:Fügen Sie dies dann Ihrem CSS hinzu, um alle untergeordneten Klassen zu inaktivieren
.inactive
:Daher sind die untergeordneten Elemente so lange inaktiv, wie der Benutzer das Element über das Feld zieht.
quelle
Ich war mit keiner der hier vorgestellten Problemumgehungen zufrieden, weil ich nicht die Kontrolle über die untergeordneten Elemente verlieren möchte.
Daher habe ich einen anderen logischen Ansatz verwendet und ihn in ein jQuery-Plugin namens jquery-draghandler übersetzt . Es manipuliert das DOM absolut nicht und garantiert hohe Leistungen. Die Verwendung ist einfach:
Das Problem wird einwandfrei gelöst, ohne dass die DOM-Funktionalität beeinträchtigt wird.
Download, Details und Erklärungen zu seinem Git-Repository .
quelle
frame
s arbeiten und Ihr Element am Rande davon steht. In diesem Fall schlage ich diesen Ansatz nicht vor.Mir gefällt eigentlich, was ich sehe https://github.com/lolmaus/jquery.dragbetter/wollte aber eine mögliche Alternative teilen. Meine allgemeine Strategie bestand darin, einen Hintergrundstil auf die Dropzone (nicht auf ihre untergeordneten Elemente) anzuwenden, wenn ich sie oder ein untergeordnetes Element (per Bubbling) per Dragenter betrete. Dann entferne ich den Stil beim Dragleaving der Drop-Zone. Die Idee war, wenn ich zu einem Kind wechsle, selbst wenn ich den Stil beim Verlassen aus der Dropzone entferne (das Dragleave wird ausgelöst), würde ich den Stil einfach erneut auf die übergeordnete Dropzone anwenden, wenn ich ein Kind dringe. Das Problem ist natürlich, dass beim Übergang von der Dropzone zu einem Kind der Dropzone der Dragenter vor dem Dragleave auf das Kind abgefeuert wird, sodass meine Stile nicht in der richtigen Reihenfolge angewendet wurden. Die Lösung für mich bestand darin, einen Timer zu verwenden, um das Dragenter-Ereignis zurück in die Nachrichtenwarteschlange zu zwingen, sodass ich es NACH dem Dragleave verarbeiten kann. Ich habe einen Abschluss verwendet, um auf das Ereignis beim Timer-Rückruf zuzugreifen.
Dies scheint in Chrom, dh Firefox, zu funktionieren und funktioniert unabhängig von der Anzahl der Kinder in der Dropzone. Ich bin etwas besorgt über das Zeitlimit, das die erneute Sequenzierung von Ereignissen garantiert, aber es scheint für meinen Anwendungsfall ziemlich gut zu funktionieren.
quelle
Ich habe versucht, dies selbst zu implementieren, und ich wollte auch weder Jquery noch ein Plugin.
Ich wollte den Datei-Upload auf folgende Weise durchführen, die für mich am besten geeignet ist:
Dateistruktur:
--- / uploads {Uploads-Verzeichnis}
--- /js/slyupload.js {Javascript-Datei.}
--- index.php
--- upload.php
--- styles.css {nur ein bisschen styling ..}
HTML Quelltext:
Dann ist dies die angehängte Javascript-Datei :: 'js / slyupload.js'
CSS:
quelle
Tut mir leid, es ist Javascript, keine Abfrage, aber für mich ist es der logischste Weg, das zu lösen. Der Browser sollte dropleave (des vorherigen Elements) vor dropenter (oder des neuen Elements) aufrufen, da etwas nichts anderes eingeben kann, bevor es das erste verlassen hat. Ich verstehe nicht, warum sie das getan haben! Sie müssen also nur verzögern de dropleave so:
Und der Dropenter wird nach dem Dropleave passieren, das ist alles!
quelle
Verwenden Sie das
greedy : true
für das Kind als Funktion vondroppable
. Starten Sie dann nur ein Ereignis, das auf die erste Ebene geklickt wurde.quelle