457 Stimmen

HTML5-Dragleave wird beim Verweilen auf einem untergeordneten Element ausgelöst

Das Problem, das ich habe, ist, dass die dragleave Ereignis eines Elements wird ausgelöst, wenn der Mauszeiger über ein untergeordnetes Element dieses Elements bewegt wird. Auch, dragenter wird nicht ausgelöst, wenn der Mauszeiger wieder zum übergeordneten Element zurückkehrt.

Ich habe eine vereinfachte Fiedel 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 dem folgenden 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");
                 }
                });

Es soll den Benutzer benachrichtigen, indem es die Drop div rot, wenn etwas dorthin gezogen wird. Das funktioniert, aber wenn Sie etwas in den Bereich p Kind, das dragleave wird ausgelöst und die div ist nicht mehr rot. Zurück zum Tropfen div macht es auch nicht wieder rot. Es ist notwendig, sich vollständig aus dem Tropfen herauszubewegen div und ziehen Sie es erneut hinein, um es rot zu machen.

Ist es möglich zu verhindern dragleave beim Ziehen in ein untergeordnetes Element nicht ausgelöst wird?

2017 Update: TL;DR, CSS nachschlagen pointer-events: none; wie in der Antwort von @H.D. unten beschrieben, die in modernen Browsern und IE11 funktioniert.

4voto

Marcin Raczkowski Punkte 1411

Ich bin über das gleiche Problem gestolpert und hier ist meine Lösung - die meiner Meinung nach viel einfacher ist als die oben genannte. Ich bin nicht sicher, ob es crossbrowser ist (könnte auf sogar Blasen Reihenfolge abhängen)

Ich werde der Einfachheit halber jQuery verwenden, aber die Lösung sollte Framework-unabhängig sein.

Das Ereignis Blasen zu Eltern so oder so gegeben:

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

Wir verbinden Ereignisse

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 alles. :) es funktioniert, denn obwohl onEnter auf Kind vor onLeave auf Elternteil feuert, verzögern wir es leicht Umkehrung der Reihenfolge, so dass Klasse zuerst entfernt wird, dann nach einer Milisekunde reaplied.

3voto

Abubakar Azeem Punkte 194

Ich habe eine einfache Lösung für dieses Problem gefunden und teile sie. Es funktioniert gut in meinem Fall.

jsfiddle Versuchen Sie es.

Sie können dies eigentlich nur über die dragenter Ereignis und Sie müssen nicht einmal eine dragleave . Alles, was Sie brauchen, ist eine Abwurfverbotszone um Ihre Abwurfzonen herum, und das war's.

Sie können auch verschachtelte Dropzones haben, und das funktioniert perfekt. Prüfen Sie auch dies verschachtelte Dropzones .

$('.dropzone').on("dragenter", function(e) {
  e.preventDefault();
  e.stopPropagation();
  $(this).addClass("over");
  $(".over").not(this).removeClass("over"); // in case of multiple dropzones
});

$('.dropzone-leave').on("dragenter", function(e) {
  e.preventDefault();
  e.stopPropagation();
  $(".over").removeClass("over");
});

// UPDATE
// As mar10 pointed out, the "Esc" key needs to be managed,
// the easiest approach is to detect the key and clean things up.

$(document).on('keyup', function(e){
  if (e.key === "Escape") {
    $(".over").removeClass("over");
  }
});

3voto

B T Punkte 51689

Ich habe ein Drag-and-Drop-Modul namens tropfnass die unter anderem dieses merkwürdige Verhalten behebt. Wenn Sie auf der Suche nach einem guten Low-Level-Drag-and-Drop-Modul sind, das Sie als Basis für alles Mögliche verwenden können (Dateiupload, In-App-Drag-and-Drop, Ziehen von oder zu externen Quellen), sollten Sie sich dieses Modul ansehen:

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

So würden Sie das tun, was Sie in drip-drop versuchen:

$('#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 Zählertechnik das, was ich in drip-drop verwendet habe, aber die am höchsten bewertete Antwort lässt wichtige Schritte aus, die dazu führen, dass die Dinge für alles außer dem ersten Tropfen kaputt gehen. Hier ist, wie man es richtig zu tun:

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

2voto

DevelJoe Punkte 666

Ich kämpfte viel mit diesem, auch nach dem Lesen durch alle diese Antworten, und dachte, ich kann meine Lösung mit Ihnen teilen, weil ich dachte, es kann eine der einfacheren Ansätze, etwas anders sein. Mein Gedanke war, einfach die dragleave Ereignis-Listener vollständig zu entfernen und das Dragleave-Verhalten mit jedem neuen ausgelösten Dragenter-Ereignis zu kodieren, wobei sichergestellt wird, dass Dragenter-Ereignisse nicht unnötig ausgelöst werden.

In meinem Beispiel unten habe ich eine Tabelle, bei der ich die Inhalte der Tabellenzeilen per Drag & Drop API untereinander austauschen möchte. Auf dragenter wird dem Zeilenelement, in das Sie Ihr Element gerade ziehen, eine CSS-Klasse hinzugefügt, um es hervorzuheben, und auf dragleave wird diese Klasse gestrichen.

Beispiel:

Sehr einfache HTML-Tabelle:

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

Und die Dragenter-Ereignishandlerfunktion, die jeder Tabellenzelle hinzugefügt wird (neben dragstart , dragover , drop y dragend Handler, die nicht spezifisch für diese Frage sind, daher hier nicht kopiert):

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

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

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

var previousRow = null;

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

Sehr einfache Kommentare im Code, so dass dieser Code auch für Anfänger geeignet ist. Ich hoffe, das wird Ihnen helfen! Beachten Sie, dass Sie natürlich alle Ereignis-Listener hinzufügen müssen, die ich oben auf jede Tabellenzelle erwähnt habe, damit dies funktioniert.

2voto

Gelöst !

Deklarieren Sie ein beliebiges Array für z.B.:

targetCollection : any[] 

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

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

Sie müssen sich nicht um untergeordnete Elemente kümmern.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X