HTML5 dragleave tiré lors de la mise en vol stationnaire d'un élément enfant

le problème que je rencontre est que l'événement dragleave d'un élément est déclenché lorsque l'élément enfant de cet élément est en vol stationnaire. De plus, dragenter n'est pas déclenché lorsque l'élément parent est de nouveau en vol stationnaire.

j'ai fait un violon simplifié: 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>

avec le JavaScript suivant:

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

Ce qu'il est censé faire est de notifier l'utilisateur en faisant la goutte div rouge lors du déplacement de quelque chose là. Cela fonctionne, mais si vous traînez dans le p enfant, le dragleave est tiré et le div n'est plus rouge. Le fait de revenir à la chute div ne le rend pas rouge non plus. Il est nécessaire de se déplacer complètement hors de la chute div et traîner à nouveau dans elle pour la rendre rouge.

est-il possible d'empêcher dragleave de tirer en se glissant dans un élément enfant?

2017 mise à Jour: TL;DR, Regarder CSS pointer-events: none; comme décrit dans la @H. D. la réponse ci-dessous qui fonctionne dans les navigateurs modernes et IE11.

224
demandé sur Gringo Suave 2011-08-18 19:53:03

28 réponses

vous avez juste besoin de garder un compteur de référence, incrémenter quand vous obtenez un dragster, décrémenter quand vous obtenez un dragleave. Lorsque le compteur est à 0-supprimer la classe.

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

Note: dans l'événement de chute, réinitialiser le compteur à zéro, et effacer la classe ajoutée.

vous pouvez l'exécuter ici

254
répondu Woody 2016-12-23 03:25:59

est-il possible d'empêcher dragleave de tirer lorsqu'on le traîne dans un élément enfant?

Oui.

#drop * {pointer-events: none;}

que les CSS semblent suffisants pour le Chrome.

tout en l'utilisant avec Firefox, le # drop ne devrait pas avoir de noeuds de texte directement (sinon il y a un étrange problème où un élément " leave it to itself" ), donc je suggère de le laisser avec un seul élément (par exemple, utiliser un div à l'intérieur de #drop de tout mettre à l'intérieur)

Voici un jsfiddle la résolution de la question d'origine (cassé) exemple .

j'ai aussi fait une version simplifiée bifurquée de l'exemple @Theodore Brown, mais basée seulement dans cette CSS.

pas tous les navigateurs ont ce CSS mis en œuvre, bien que: http://caniuse.com/pointer-events

en voyant le code source de Facebook, je pourrais trouver ce pointer-events: none; plusieurs fois, mais il est probablement utilisé avec des retombées de dégradation gracieuse. Au moins, c'est si simple et résout le problème pour beaucoup d'environnements.

51
répondu H.D. 2017-05-23 12:10:33

ici, la solution cross-Browser la plus simple (sérieusement):

jsfiddle <-- essayez de faire glisser un fichier à l'intérieur de la boîte

vous pouvez faire quelque chose comme ça:

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

en quelques mots, vous créez un" masque " à l'intérieur de la dropzone, avec la largeur et la hauteur héritées, la position absolue, qui montrera juste quand le dragover démarre.

Donc, après avoir montré ce masque, vous pouvez faire le trick en attachant les autres événements dragleave & drop dessus.

après avoir quitté ou tomber, vous cachez juste le masque à nouveau.

Simple et sans complications.

(Obs.: Greg Pettit conseils -- Vous devez être sûr que le masque placez l'ensemble de la zone, y compris les frontaliers)

38
répondu Diego T. Yamaguchi 2014-01-17 02:14:43

la" bonne " façon de résoudre ce problème est de désactiver les événements pointeurs sur les éléments enfants de la cible de chute (comme dans la réponse de @H. D.). voici un jsFiddle que j'ai créé qui démontre cette technique . Malheureusement, cela ne fonctionne pas dans les versions D'Internet Explorer antérieures à IE11, car ils ne supportaient pas les événements pointeurs .

heureusement, j'ai pu trouver une solution qui fait travail dans les anciennes versions D'IE. Fondamentalement, il s'agit d'identifier et d'ignorer les événements dragleave qui se produisent en traînant sur les éléments enfants. Étant donné que l'événement dragenter est lancé sur les noeuds enfants avant l'événement dragleave sur le parent, des écouteurs d'événements séparés peuvent être ajoutés à chaque noeud enfant qui ajoute ou supprime une classe "ignore-drag-leave" de la cible de chute. Ensuite, l'écouteur d'événements dragleave de la cible de chute peut tout simplement ignorer les appels qui se produisent lorsque cette classe existe. Voici un jsFiddle la démonstration de cette solution de contournement . Il est testé et fonctionne en Chrome, Firefox et IE8+.

mise à jour:

j'ai créé un jsFiddle démontrant une solution combinée en utilisant la détection de fonctionnalité, où les événements pointeur sont utilisés si pris en charge (actuellement Chrome, Firefox, et IE11), et le navigateur retombe à ajouter des événements aux noeuds enfants si le soutien d'événement pointeur n'est pas disponible (IE8-10).

29
répondu Theodore Brown 2013-09-18 22:19:54

Cela fait un certain temps que cette question est posée et beaucoup de solutions (y compris des pirouettes laides) sont proposées.

j'ai réussi à corriger le même problème que j'avais récemment grâce à la réponse dans ce réponse et j'ai pensé qu'il peut être utile à quelqu'un qui vient à travers à cette page. L'idée est de stocker le evenet.target dans ondrageenter chaque fois qu'il est appelé sur l'un des parents ou l'enfant élément. Ensuite, dans ondragleave , vérifiez si la cible courante ( event.target ) est égale à l'objet que vous avez stocké dans ondragenter .

le seul cas où ces deux cas sont appariés est lorsque votre traînée quitte la fenêtre du navigateur.

la raison pour laquelle cela fonctionne bien est lorsque la souris quitte un élément (dire el1 ) et entre dans un autre élément (dire el2 ), d'abord le el1.ondragenter est appelé et puis el2.ondragleave . Seulement quand la traînée est en sortant/entrant dans la fenêtre du navigateur, event.target sera '' dans les deux el1.ondragenter et el2.ondragleave .

Voici mon échantillon de travail. Je l'ai testé sur IE9+, Chrome, Firefox et Safari.

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

et voici une page html simple:

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

avec un style approprié ce que j'ai fait est de rendre la div interne (#file-drop-area) beaucoup plus grand chaque fois qu'un fichier est traîné dans l'écran de sorte que l'utilisateur peut facilement déposer les fichiers à l'endroit approprié.

23
répondu Ali Motevallian 2017-09-12 01:24:11

le problème est que l'événement dragleave est déclenché lorsque la souris passe devant l'élément enfant.

j'ai essayé diverses méthodes de vérification pour voir si l'élément e.target est le même que l'élément this , mais n'a pas pu obtenir d'amélioration.

la façon dont j'ai corrigé ce problème était un peu un piratage, mais fonctionne à 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
répondu Greg 2012-07-18 19:26:13

vous pouvez le fixer dans Firefox avec un peu d'inspiration du code source jQuery :

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

    if (related !== this) {

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

        if (!inside) {

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

}

malheureusement, il ne fonctionne pas dans Chrome parce que relatedTarget semble ne pas exister sur dragleave événements, et je suppose que vous travaillez dans Chrome parce que votre exemple ne fonctionne pas dans Firefox. Voici une version avec le code ci-dessus implémenté.

8
répondu robertc 2011-08-19 00:57:28

une solution très simple est d'utiliser la pointer-events propriété CSS . Il suffit de mettre sa valeur none sur dragstart sur chaque élément enfant. Ces éléments ne déclencheront plus d'événements liés à la souris, de sorte qu'ils n'attraperont plus la souris au-dessus d'eux et ne déclencheront donc pas le dragleave sur le parent.

n'oubliez pas de ramener cette propriété à auto en finissant la traînée ;)

8
répondu FruityFred 2017-04-25 09:35:22

et voilà, une solution pour le 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
répondu Aldekein 2012-10-12 12:18:17

Voici une autre solution utilisant le document.elementFromPoint:

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

Espère que cela fonctionne, voici une violon .

6
répondu marcelj 2016-01-31 17:49:33

j'ai écrit une petite bibliothèque appelée Dragster pour gérer cette question exacte, fonctionne partout sauf silencieusement ne rien faire dans IE (qui ne prend pas en charge les constructeurs D'événements DOM, mais il serait assez facile d'écrire quelque chose de similaire à l'aide des événements personnalisés de jQuery)

5
répondu Ben 2013-11-14 09:40:28

Je ne suis pas sûr que ce navigateur croisé, mais j'ai testé dans Chrome et il résout mon problème:

je veux faire glisser et déposer un fichier sur toute la page, mais mon dragleave est déclenché lorsque je traîne un élément enfant. Ma solution était de regarder le x et le y de la souris:

j'ai une div qui recouvre toute ma page, quand la page se charge je la cache.

quand vous faites glisser le document je le montre, et quand vous tombez sur le parent il le gère, et quand tu quittes le parent, je vérifie x et 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
répondu chrisallick 2012-09-18 21:56:41

j'avais le même problème et j'ai essayé d'utiliser la solution pk7s. Il a fonctionné, mais il pourrait être fait un peu mieux sans éléments dom supplémentaires.

fondamentalement, l'idée est la même - ajouter une superposition non visible supplémentaire sur la zone droppable. Permet seulement de le faire sans aucun élément supplémentaire dom. Voici la partie où les pseudo-éléments CSS sont venus jouer.

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

après la règle créera un recouvrement entièrement couvert pour la zone droppable.

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

Voici la solution complète: http://jsfiddle.net/F6GDq/8 /

j'espère que ça aidera n'importe qui avec le même problème.

4
répondu rasmusx 2014-02-18 09:23:45

je suis tombé dans le même problème et voici ma solution - qui je pense est beaucoup plus facile que ci-dessus. Je ne suis pas sûr si c'est crossbrowser (pourrait dépendre de même ordre de bouillonnement)

je vais utiliser jQuery pour la simplicité, mais la solution doit être Cadre indépendant.

l'événement bulles à parent dans l'un ou l'autre sens ainsi donné:

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

Nous associer des événements

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

Et c'est tout. :) il fonctionne parce que même si onEnter sur les feux de l'enfant avant de onLeave sur parent, nous le retardons légèrement Inverser l'ordre, donc la classe est enlevée d'abord puis reaplied après une milliseconde.

3
répondu Marcin Raczkowski 2013-06-11 19:45:42

il suffit de vérifier si l'élément traîné est un enfant, si c'est le cas, alors ne retirez pas votre classe de style "dragover". Assez simple et fonctionne pour moi:

 $yourElement.on('dragleave dragend drop', function(e) {
      if(!$yourElement.has(e.target).length){
           $yourElement.removeClass('is-dragover');
      }
  })
3
répondu sofarsoghood 2017-06-09 11:19:24

si vous utilisez HTML5, vous pouvez obtenir la directive client du parent:

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

puis dans le parent.dragleave ():

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

voici un jsfiddle

3
répondu azlar 2018-01-31 14:49:12

une solution de travail alternative, un peu plus simple.

//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
répondu Profet 2013-07-14 09:43:02

après avoir passé tant d'heures, j'ai eu cette suggestion qui fonctionnait exactement comme prévu. Je voulais fournir une indication seulement quand les fichiers ont été traînés plus de, et document dragover, dragleave causait des clignotements douloureux sur le navigateur Chrome.

C'est comme ça que je l'ai résolu, en jetant aussi des indices appropriés pour l'utilisateur.

$(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
répondu visitsb 2013-09-09 16:02:23

j'ai écrit un glisser-déposer le module appelé goutte à goutte qui corrige ce comportement bizarre, entre autres. Si vous êtes à la recherche d'un bon module de drag-and-drop de bas niveau que vous pouvez utiliser comme base pour n'importe quoi (téléchargement de fichier, in-app drag-and-drop, glisser à partir ou vers des sources externes), vous devriez vérifier ce module:

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

C'est comme ça que tu ferais vous essayez de faire dans drip-drop:

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

pour faire cela sans bibliothèque, la technique du compteur est ce que j'ai utilisé dans drip-drop, tho la réponse la mieux cotée manque des étapes importantes qui causeront des choses à casser pour tout sauf la première goutte. Voici comment le faire correctement:

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
répondu B T 2016-07-18 08:40:35

j'ai eu un problème similaire - mon code pour cacher la dropzone sur l'événement dragleave pour le corps a été tiré de manière contestataire lorsque des éléments enfants en vol stationnaire faisant le flicker dropzone dans Google Chrome.

j'ai pu résoudre cela en programmant la fonction pour cacher dropzone au lieu de l'appeler immédiatement. Ensuite, si un autre draggover ou dragleave est déclenché, l'appel de fonction programmé est annulé.

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
répondu Arseny 2014-12-01 00:01:59

une solution simple consiste à ajouter la règle css pointer-events: none au composant enfant pour éviter le déclenchement de ondragleave . Voir Exemple:

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</p>
</div>
1
répondu Alexandre Annic 2018-08-24 08:29:43

Voici une autre approche basée sur le calendrier des événements.

l'événement dragenter expédié de l'élément enfant peut être capturé par l'élément parent et il se produit toujours avant le dragleave . Le timing entre ces deux événements est vraiment court, plus court que n'importe quelle action humaine de la souris. Ainsi, l'idée est de mémoriser le moment où un dragenter se produit et filtrer dragleave événements qui se produit "pas trop vite" après ...

cet exemple court fonctionne sur Chrome et 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
répondu smrtl 2013-05-01 13:28:51

pimvdb..

pourquoi ne pas essayer d'utiliser drop au lieu de dragleave . Il a travaillé pour moi. espérons que cela résout votre problème.

s'il vous plaît vérifier le 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
répondu abhi 2014-01-03 09:53:39

vous pouvez utiliser un timeout avec un drapeau transitioning et écouter sur l'élément supérieur. dragenter / dragleave de l'enfant événements va bulle jusqu'au conteneur.

depuis dragenter sur l'élément enfant feux avant dragleave du conteneur, nous allons mettre le drapeau montrer comme transition pour 1ms... l'auditeur dragleave vérifiera le drapeau avant que le 1ms ne soit activé.

le drapeau ne sera vrai que pendant les transitions à l'enfant éléments, et ne sera pas le cas lors de la transition à un élément parent (le contenant)

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
répondu ilovett 2014-07-23 21:28:39

Vous devez supprimer les événements de pointeur pour tous les objets enfants de la cible de glisser.

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
répondu EddieB 2015-06-10 14:45:58

utilisez ce 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
répondu Mohammad Rezaei 2016-06-20 09:17:54

essayez d'utiliser l'événement.eventPhase. Il sera fixé à 2 (Événement.AT_TARGET) uniquement si la cible ist entré, sinon, il est fixé à 3 (Événement.BUBBLING_PHASE).

l'Efe a utilisé l'eventPhase pour lier ou délier l'événement dragleave.

$('.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
répondu GNaruhn 2016-07-05 09:36:20

j'ai trouvé une solution similaire mais plus élégante à la réponse de @azlar, et c'est ma solution:

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

cette méthode détecte si la souris a quitté la page. Il fonctionne bien dans le Chrome et le bord.

0
répondu zysite 2018-02-13 18:26:39