'Dragleave' des übergeordneten Elements wird beim Ziehen über untergeordnete Elemente ausgelöst


163

Überblick

Ich habe die folgende HTML-Struktur und habe die dragenterund 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 dragenterEreignis wie erwartet ausgelöst. Wenn ich jedoch mit der Maus über ein untergeordnetes Element bewege, z. B. <div id="drag-n-drop">, wird das dragenterEreignis für das <div id="drag-n-drop">Element und dann das dragleaveEreignis für das <div id="dropzone">Element ausgelöst .

Wenn ich den Mauszeiger erneut über das <div id="dropzone">Element bewege, wird das dragenterEreignis erneut ausgelöst, was cool ist, aber dann wird das dragleaveEreignis für das gerade verbleibende untergeordnete Element ausgelöst, sodass die removeClassAnweisung ausgeführt wird, die nicht cool ist.

Dieses Verhalten ist aus zwei Gründen problematisch:

  1. 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.

  2. Ich ziehe immer noch über das <div id="dropzone">Element, während ich über seinen Kindern schwebe, damit ich nicht dragleaveschieß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 dragleavenicht 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 dragleaveEreignis 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.


Verhindern Sie die Weitergabe von Ereignissen übere.stopPropagation();
Blender

Ich denke ich bin schon ... schau dir das Update an
Hristo

Können Sie irgendwo auf jsfiddle.net eine Demo posten, an der die Leute basteln können?
Blender

@Blender ... sicher, gib mir ein paar Minuten!
Hristo

@Blender ... Ich habe meine Frage mit einer Geige aktualisiert
Hristo

Antworten:


171

Wenn Sie keine Ereignisse an die untergeordneten Elemente binden müssen, können Sie immer die Eigenschaft pointer-events verwenden.

.child-elements {
  pointer-events: none;
}

4
Beste Lösung! Ich hätte nicht einmal gedacht, dass ich das Problem mit CSS auf so elegante Weise lösen könnte. Vielen Dank!
Serg66

1
Oh ja. Das ist es!
mcmlxxxiii

23
Der einzige Nachteil dieses Ansatzes besteht darin, dass alle Zeigerereignisse für untergeordnete Elemente zerstört werden (z. B. können sie keine separaten :hoverStile 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
Ben

2
Kombinieren Sie mit CSS Child Selector, um alle Kinder Ihrer Dropzone zu erreichen: .dropzone * {pointer-events: none;}
Philipp F

7
Sie verwenden jQuery bereits, also nur $('.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.
Gavin

56

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 #dropzoneElement 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:

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

Dies funktioniert ziemlich gut, aber in Firefox sind Probleme aufgetreten, da Firefox doppelt aufgerufen wurde dragenterund 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:

$.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;
    }
});

Dies ist sauber und elegant und scheint in jedem Browser zu funktionieren, den ich bisher getestet habe (ich habe den IE noch nicht getestet).


Dies ist die bisher beste Lösung. Das einzige Problem ist, dass dieser Code nicht funktioniert, wenn Sie eine Datei hinzufügen, entfernen und eine andere Datei darüber ziehen. Erst nach dem Auslösen von HoverEnd und dem erneuten Auslösen von HoverStart funktioniert dieser Code wieder.
MysticEarth

@MysticEarth Kannst du eine jsFiddle zur Demo zusammenstellen, wenn es nicht funktioniert?
Hristo

1
Vielen Dank für Ihre Antwort. Dies war sehr hilfreich, da die HTML5-Drag & Drop-Oberfläche und die Ereignis-Listener sehr schmerzhaft sein können. Dies ist der beste Weg, um mit den verrückten Ereignissen umzugehen, insbesondere wenn Sie mehrere Instanzen von Drop-Zone-Elementen behandeln möchten.
Nico O

Warum müssen wir stopPropagation () PreventDefault () aufrufen und false zurückgeben? Die Rückgabe false sollte ausreichen. Sollte es ?
Dejo

Es ist redundant, ruft aber an stopPropagation()und preventDefault()wird bevorzugt. Ich würde das fallen lassen return false.
Peeja

38

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'.

(function () {

    var droppable = $('#droppable'),
        lastenter;

    droppable.on("dragenter", function (event) {
        lastenter = event.target;
        droppable.addClass("drag-over");            
    });

    droppable.on("dragleave", function (event) {
        if (lastenter === event.target) {
            droppable.removeClass("drag-over");
        }
    });

}());

Ich werde es später versuchen. Wenn überhaupt, sieht es sauberer aus als meine Lösung, aber ich kann nicht sicher sein, wie browserübergreifend es ist, wenn ich es mir nur ansehe. Wo hast du das getestet?
Hristo

1
Nur Chrome, und möglicherweise nur unter der Annahme, dass zwischen den untergeordneten Elementen und dem Container eine physische Lücke besteht.
Hacklikecrack

5
Meine Lieblingsantwort hier. Fühlt sich überhaupt nicht hässlich an :)
Matt Way

1
Funktioniert für mich in IE, Chrome, FF
Vicentiu Bacioiu

1
Dies funktioniert nicht, wenn das Ziehen an den untergeordneten Elementen beginnt. Zum Beispiel, wenn Sie aus einem anderen Fenster so ziehen, dass das Ziehen innerhalb des untergeordneten Elements beginnt.
FINDarkside

29

Zuerst stimmte ich den Leuten zu, die den pointer-events: noneAnsatz verwarfen . Aber dann habe ich mich gefragt:

Benötigen Sie wirklich Zeigerereignisse, um an den untergeordneten Elementen zu arbeiten, während das Ziehen ausgeführt wird ?

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:

  div.drag-target-parent-container.dragging-in-progress * {
    pointer-events: none;
  }

Verwenden Sie Ihren bevorzugten Ansatz, um die Klasse dragging-in-progressin den dragEnter/ dragLeaveevent-Handlern hinzuzufügen / zu entfernen , wie ich es in dragStartet. al.


1
Diese Lösung ist sowohl die einfachste als auch (soweit ich das beurteilen kann) vollständig beweiskräftig. Fügen Sie einfach die Klasse in das dragstartEreignis ein und entfernen Sie sie in dragend.
Mann

Dies ist eine ziemlich schöne Lösung, aber nicht perfekt. Wenn das Ziehen des untergeordneten Elements beginnt (Ziehen aus einem anderen Fenster), wird dragEnter niemals ausgelöst. Es ist wahrscheinlich auch möglich, die Maus schnell genug zu bewegen, damit der dragEnter nicht ausgelöst wird, wenn Sie zum Kind wechseln, aber nicht 100% sicher sind.
FINDarkside

12

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 :

$(document).ready(function() {
    var dropzone = $('#overlay');

    dropzone.on('dragenter', function(event) {
        $('#dropzone-highlight').addClass('dnd-hover');
    });

    dropzone.on('dragleave', function(event) {
        $('#dropzone-highlight').removeClass('dnd-hover');
    });

});​

HTML :

<div id="dropzone-highlight">
    <div id="overlay"></div>

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

<h2 draggable="true">Drag me</h2>

Danke für die Antwort, Blender. Ich habe dies jedoch bereits versucht. Das Problem bei dieser Lösung ist, dass das Overlay-Element die untergeordneten Elemente "überlagert", mit denen ich interagieren muss. Wenn Sie also ein Overlay haben, wird die Interaktion, die ich mit den untergeordneten Elementen benötige, deaktiviert. Stellen Sie sich das als Datei-Drag-n vor -drop box ... Sie können ziehen und ablegen oder auf das Eingabeelement klicken, um Ihre Dateien auszuwählen.
Hristo

Hier ist ein Beispiel für eine Geige, die Ihre Lösung nicht erfüllt ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo

Ich bin verwirrt. Warum müssen Sie auch mit den untergeordneten Elementen interagieren?
Blender

In meinem speziellen Fall ist ein untergeordnetes Element der "Dropzone" eine Dateiauswahlschaltfläche. Wenn ich also keine Dateien ziehen und ablegen möchte, kann ich sie mithilfe des Eingabeelements auswählen. Aber mit der Überlagerung ... kann ich nicht auf das Eingabeelement klicken. Sinn ergeben?
Hristo

@Blender In den meisten Fällen müssen wir eine Datei vom Desktop ziehen, kein Element von der Seite, daher funktioniert dies nicht.
Mārtiņš Briedis

5

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.

$("#dropzone,#dropzone *").on('dragenter', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

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:

$("#dropzone,#dropzone *").on('dragenter dragover', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

Definieren Sie das Dragleave-Ereignis nur für die Dropzone.


2
das wird das Problem jedoch nicht lösen. Das eigentliche Problem ist dragleave... wenn ich ein Kind verlasse, bin ich vielleicht immer noch bei den Eltern, #dropzoneaber das wird das dragenterEreignis nicht wieder auslösen :(
Hristo

Sind Sie sicher, dass der Dragenter nicht erneut gefeuert wird?
Oliver

Ah, richtig ... es wird erneut ausgelöst, aber nachdem es ausgelöst wurde, wird das dragleaveEreignis für das gerade verlassene untergeordnete Element ausgelöst, sodass die removeClassAnweisung erneut ausgeführt wird
Hristo

Ich kann nicht dragleavenur für die definieren #dropzone... wenn ich könnte, hätte ich dieses Problem nicht.
Hristo

Nein, ich wollte den Kindern keine Ereignisse für Dragleave hinzufügen.
Oliver

4

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:

#dropzone.dragover *{
   pointer-events: none;
}

Und fügen Sie diesen Ihrem JS-Code hinzu:

$("#dropzone").on("dragover", function (event) {
   $("#dropzone").addClass("dragover");
});

$("#dropzone").on("dragleave", function (event) {
   $("#dropzone").removeClass("dragover");
});

2
Für mich führt dies dazu, dass die Überlagerung ständig blinkt.
Andrey Mikhaylov - lolmaus

3

Wenn Sie jQuery verwenden, überprüfen Sie dies: https://github.com/dancork/jquery.event.dragout

Es ist wirklich großartig.

Spezielles Ereignis für echte Dragleave-Funktionen.

Das HTML5-Dragleave-Ereignis funktioniert eher wie ein Mouseout. Dieses Plugin wurde erstellt, um die Funktionen des Mausblatts beim Ziehen zu replizieren.

Anwendungsbeispiel:

$ ('# myelement'). on ('dragout', Funktion (Ereignis) {// IHR CODE});

EDIT: Eigentlich glaube ich nicht, dass es von jQuery abhängt, Sie können den Code wahrscheinlich auch ohne ihn verwenden.


Beeindruckend! Dragout ist die EINE Sache, die für mein Layout mit vielen dragleaveuntergeordneten Elementen funktioniert hat , obwohl ich die übergeordnete Dropzone nicht verlassen habe.
Mark Kasson

Nachdem dragoutich die letzte Stunde mit diesem Problem gekämpft hatte, war es die einzige Lösung, die für mich funktioniert hat.
Jason Hazel

2

@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.

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

Ich nehme an, Sie vermissen die Definitionen für einige Variablen außerhalb des Gültigkeitsbereichsvar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen,

@ TimoHuovinen ja, richtig. Beide dropZoneHideDelay, dropZoneVisiblewerden für zwei 'on'Dokumentereignisse in meiner Implementierung benötigt.
Visitsb

2

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

<div class="drop-zone">
  <h2 class="drop-here">Drop here</h2>
  <h2 class="drop-now">Drop now!</h2>
  <p>Or <a href="#">browse a file</a></p>
  <div class="drop-layer"></div>
</div>

CSS

.drop-zone{
  padding:50px;
  border:2px dashed #999;
  text-align:center;
  position:relative;
}
.drop-layer{
  display:none;
  position:absolute;
  top:0;
  left:0;
  bottom:0;
  right:0;
  z-index:5;
}
.drop-now{
  display:none;
}

JS

$('.drop-zone').on('dragenter', function(e){
    $('.drop-here').css('display','none');
    $('.drop-now').css('display','block');
    $(this).find('.drop-layer').css('display','block');
    return false;
});

$('.drop-layer').on('dragleave', function(e){
    $('.drop-here').css('display','block');
    $('.drop-now').css('display','none');
    $(this).css('display','none');
    return false;
});

Einfach und effektiv! Ich bin die stopPropagation () -Route gegangen und habe es nicht gemocht, den Event-Handler auf alle Kinder anzuwenden. Es schien chaotisch, aber das funktioniert gut und ist einfach zu implementieren. Gute Idee.
Jon Catmull

1

Für mich hat der Ansatz pointer-events: none;also nicht so gut funktioniert ... Hier ist meine alternative Lösung:

    #dropzone {
        position: relative;
    }

    #dropzone(.active)::after {
        position: absolute;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
        content: '';
    }

Auf diese Weise ist es dragleavedem übergeordneten dragoverElement ( einem untergeordneten Element ) oder einem untergeordneten Element nicht möglich . hoffe das hilft :)

* Die '.active'-Klasse, die ich hinzufüge, wenn ich dragenteroder dragleave. Aber wenn Sie ohne das arbeiten, lassen Sie die Klasse einfach weg.


1
Ich habe dies in einer EmberJS D & D-Komponente versucht und das programmgesteuerte Hinzufügen der 'aktiven' Klasse ist in etwa 40% der Zeit, in der die Maus in das übergeordnete Div (Ember-Ansicht) eintritt, zu langsam. Nicht sicher warum. Aber lassen Sie das weg und es funktioniert großartig. Vielen Dank für diese einfache Lösung. Getestet auf einem Mac mit den neuesten Versionen (ab heute) von Safari, Chrome, Firefox.
rmcsharry

1

Super einfache schnelle Lösung dafür, nicht ausgiebig getestet, funktioniert aber gerade in Chrome.

Entschuldigen Sie das Coffeescript.

  dragEndTimer = no

  document.addEventListener 'dragover', (event) ->
    clearTimeout dragEndTimer
    $('body').addClass 'dragging'
    event.preventDefault()
    return no

  document.addEventListener 'dragenter', (event) ->
    $('section').scrollTop(0)
    clearTimeout dragEndTimer
    $('body').addClass 'dragging'

  document.addEventListener 'dragleave', (event) ->
    dragEndTimer = setTimeout ->
      $('body').removeClass 'dragging'
    , 50

Dies behebt den Chrome-Flimmerfehler oder zumindest die Permutation, die mir Probleme bereitete.



1

Meine Version:

$(".dropzone").bind("dragover", function(e){
    console.log('dragover');
});

$(".dropzone").bind("dragleave", function(e) {
  var stopDrag = false;
  if (!e.relatedTarget) stopDrag = true;
  else {
    var parentDrop = $(e.relatedTarget).parents('.dropzone');
    if (e.relatedTarget != this && !parentDrop.length) stopDrag = true;
  }

  if (stopDrag) {
    console.log('dragleave');
  }
});

Mit diesem Layout:

<div class="dropzone">
  <div class="inner-zone">Inner-zone</div>
</div>

Ich habe einen Dump von Elementklassen für e.target, e.currentTarget, e.relatedTargetfür beide dragoverunddragleave Veranstaltungen.

Es hat mir gezeigt, dass beim Verlassen des übergeordneten Blocks ( .dropzone) e.relatedTargetkein Kind dieses Blocks ist, sodass ich weiß, dass ich mich außerhalb der Dropzone befinde.


0

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:

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

Damit sollten Sie bereits wissen, was hier passiert.
Schau dir nur die Geige an, weißt du?


0

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:

function drop(ev) { 
ev.preventDefault(); 
data=ev.dataTransfer.getData('Text'); 
if(ev.target=="[object HTMLLIElement]")  
{ev.target.parentNode.appendChild(document.getElementById(data));}
else{ev.target.appendChild(document.getElementById(data));} 
} 

0

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 divmit ID #drop. Fügen Sie dies Ihrem Javascript hinzu:

$('#drop').on('dragenter', function() {
    $(this).addClass('dragover');
    $(this).children().addClass('inactive');
});

$('#drop').on('dragleave', function() {
    $(this).removeClass('dragover');
    $(this).children().removeClass('inactive');
});

Fügen Sie dies dann Ihrem CSS hinzu, um alle untergeordneten Klassen zu inaktivieren .inactive:

#drop *.inactive {
    pointer-events: none;
}

Daher sind die untergeordneten Elemente so lange inaktiv, wie der Benutzer das Element über das Feld zieht.


0

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:

$(document).ready(function() {

    $(selector).draghandler({
        onDragEnter: function() {
            // $(this).doSomething();
        },
        onDragLeave: function() {
            // $(this).doSomethingElse();
        }
    });

});

Das Problem wird einwandfrei gelöst, ohne dass die DOM-Funktionalität beeinträchtigt wird.

Download, Details und Erklärungen zu seinem Git-Repository .


Wie erhalte ich das Drag-Enter-Ereignis in Ihrem Plugin?
Woody

Ich habe Ihre Direktive noch nicht getestet, aber ich denke, es funktioniert nicht, wenn das übergeordnete Element (A) das Element, nach dem ich suchen möchte (B), nicht wirklich umgibt. Wenn zum Beispiel zwischen der Unterkante von B und der Unterkante von A keine Polsterung vorhanden ist, wird der Dragenter niemals für Element A ausgelöst, oder?
Marcelj

@marcelj Element A kann das Body-Dokument selbst sein, sodass Sie kein umgebendes Element (Body erledigt den Job) mit minimalem Abstand benötigen. Die Einschränkung, die ich später bei diesem Ansatz festgestellt habe, besteht darin, dass Sie mit frames arbeiten und Ihr Element am Rande davon steht. In diesem Fall schlage ich diesen Ansatz nicht vor.
Luca Fagioli

0

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.

$('.dropzone').on('dragenter', function(event) {
  (function (event) {
    setTimeout(function () {
      $(event.target).closest('.dropzone').addClass('highlight');
    }, 0);
  }) (event.originalEvent); 
});

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.


0

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:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>my Dzone Upload...</title>
<link rel="stylesheet" href="styles.css" />
</head>

<body>
    <div id="uploads"></div>        
    <div class="dropzone" id="dropzone">Drop files here to upload</div>     
    <script type="text/javascript" src="js/slyupload.js"></script>      
</body>
</html>

Dann ist dies die angehängte Javascript-Datei :: 'js / slyupload.js'

<!-- begin snippet:  -->

<!-- language: lang-js -->

    // JavaScript Document

    //ondragover, ondragleave...


    (function(){        

        var dropzone = document.getElementById('dropzone');
        var intv;

        function displayUploads(data){
            //console.log(data, data.length);
            var uploads = document.getElementById('uploads'),anchor,x;

            for(x=0; x < data.length; x++){

                //alert(data[x].file);
                anchor = document.createElement('a');
                anchor.href = data[x].file;
                anchor.innerText = data[x].name;

                uploads.appendChild(anchor);
            }               
        }

        function upload(files){
            //console.log(files);
            var formData = new FormData(), 
                xhr      = new XMLHttpRequest(),    //for ajax calls...
                x;                                  //for the loop..

                for(x=0;x<files.length; x++){
                    formData.append('file[]', files[x]);

                    /*//do this for every file...
                    xhr = new XMLHttpRequest();

                    //open... and send individually..
                    xhr.open('post', 'upload.php');
                    xhr.send(formData);*/
                }

                xhr.onload = function(){
                    var data = JSON.parse(this.responseText);   //whatever comes from our php..
                    //console.log(data);
                    displayUploads(data);

                    //clear the interval when upload completes... 
                    clearInterval(intv);
                }                   

                xhr.onerror = function(){
                    console.log(xhr.status);
                }

                //use this to send all together.. and disable the xhr sending above...

                //open... and send individually..
                intv = setInterval(updateProgress, 50);
                xhr.open('post', 'upload.php');
                xhr.send(formData);

                //update progress... 
                 /* */                   
        }

        function updateProgress(){
            console.log('hello');
         }          

        dropzone.ondrop = function(e){
            e.preventDefault(); //prevent the default behaviour.. of displaying images when dropped...
            this.className = 'dropzone';
            //we can now call the uploading... 
            upload(e.dataTransfer.files); //the event has a data transfer object...
        }

        dropzone.ondragover = function(){
            //console.log('hello');
            this.className = 'dropzone dragover';
            return false;
        }

        dropzone.ondragleave = function(){
            this.className = 'dropzone';
            return false;
        }           
    }(window));

CSS:

body{
	font-family:Arial, Helvetica, sans-serif; 
	font-size:12px;
}

.dropzone{
	width:300px; 
	height:300px;
	border:2px dashed #ccc;
	color:#ccc;
	line-height:300px;
	text-align:center;
}

.dropzone.dragover{
	border-color:#000;
	color:#000;
}


0

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:

function mydropleave(e)
{
    e.preventDefault();
    e.stopPropagation();

    setTimeout(function(e){ //the things you want to do },1);
}

Und der Dropenter wird nach dem Dropleave passieren, das ist alles!


-1

Verwenden Sie das greedy : truefür das Kind als Funktion von droppable. Starten Sie dann nur ein Ereignis, das auf die erste Ebene geklickt wurde.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.