175 votes

'dragleave' de l'élément parent feux lorsque vous faites glisser des éléments sur les enfants

Vue d'ensemble

J'ai le code HTML suivant la structure et j'ai attaché l' dragenter et dragleave événements à l' <div id="dropzone"> élément.

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

Problème

Lorsque je fais glisser un fichier sur l' <div id="dropzone">, dragenter événement est déclenché comme prévu. Cependant, lorsque je passe ma souris sur un élément enfant, tels que <div id="drag-n-drop">, dragenter événement est déclenché pour l' <div id="drag-n-drop"> élément et ensuite l' dragleave événement est déclenché pour l' <div id="dropzone"> élément.

Si j'ai passez la souris sur l' <div id="dropzone"> élément nouveau, l' dragenter événement est de nouveau tiré, ce qui est cool, mais alors l' dragleave événement est déclenché pour l'élément enfant juste à gauche, de sorte que le removeClass instruction est exécutée, ce qui n'est pas cool.

Ce comportement est problématique pour 2 raisons:

  1. Je suis le seul à en fixer dragenter & dragleave de la <div id="dropzone"> donc je ne comprends pas pourquoi les enfants des éléments de ces événements.

  2. Je suis toujours en faisant glisser le pointeur sur l' <div id="dropzone"> élément, tout en planant au-dessus de ses enfants donc je ne veux pas dragleave de le feu!

jsFiddle

Voici un jsFiddle pour bricoler avec: http://jsfiddle.net/yYF3S/2/

Question

Alors... comment puis-je faire pour que lorsque je suis en faisant glisser un fichier sur l' <div id="dropzone"> élément, dragleave ne se déclenche pas même si je suis en faisant glisser le pointeur sur tous les enfants des éléments... il faut que du feu quand je sors de l' <div id="dropzone"> élément... planant/glisser n'importe où dans les limites de l'élément ne doit pas déclencher l' dragleave événement.

J'ai besoin de cela pour être compatible avec tous les navigateurs, au moins dans les navigateurs qui le supportent HTML5 drag-n-drop, de sorte que cette réponse n'est pas adéquate.

Il semble que Google et Dropbox ont compris cela, mais leur code source est minimisé/complexe, donc je n'ai pas été en mesure de le comprendre à partir de leur mise en œuvre.

179voto

benr Points 1021

Si vous n'avez pas besoin de lier les événements à l'enfant-éléments, vous pouvez toujours utiliser le pointeur d'événements de la propriété.

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

59voto

Hristo Points 12268

J'ai finalement trouvé une solution, je suis heureux avec. J'ai effectivement trouvé plusieurs façons de faire ce que je veux, mais aucun n'a été aussi réussie que la solution actuelle... dans une solution, j'ai connu fréquentes scintille comme un résultat de l'ajout/retrait d'une frontière à l' #dropzone élément... dans un autre, la frontière n'a jamais été supprimé si vous passez à l'écart à partir du navigateur.

De toute façon, mon meilleur hacky solution est ceci:

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

Cela fonctionne assez bien mais les problèmes sont revenus dans Firefox, car Firefox a été une double invocation dragenter donc mon compteur était à l'arrêt. Mais néanmoins, ce n'est pas très élégant.

Puis je suis tombé sur cette question: Comment détecter les dragleave événement dans Firefox lors du déplacement à l'extérieur de la fenêtre

J'ai donc pris la réponse et de l'appliquer à ma situation:

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

C'est propre et élégant et semble fonctionner dans tous les navigateurs que j'ai testé jusqu'à présent (n'ai pas testé IE encore).

41voto

hacklikecrack Points 458

C'est un peu moche mais ça marche nom de dieu!...

Sur votre "dragenter' gestionnaire de magasin de l'événement.cible (dans une variable à l'intérieur de votre clôture, ou quoi que ce soit), puis dans votre 'dragleave' gestionnaire d'incendie de votre code si l'événement.cible === celui que vous avez stockées.

Si votre "dragenter" est généré lorsque vous ne voulez pas qu'elle (c'est à dire lorsqu'elle est saisie après la sortie de l'enfant-éléments), puis la dernière fois qu'il se déclenche avant que la souris quitte le parent, le parent, de sorte que le parent sera toujours la finale de "dragenter' avant le '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");
        }
    });

}());

12voto

Blender Points 114729

Cela semble être un Chrome bug.

La seule solution que je pouvais penser était de créer une superposition transparente élément de capture de vos événements: 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>
​

5voto

Oliver Points 1814

Le problème, c'est que vos éléments à l'intérieur de la centres sont bien sûr partie de la dropzone et lorsque vous entrez les enfants, vous quittez le parent. La résolution de ce n'est pas facile. Vous pouvez essayer d'ajouter des événements pour les enfants aussi l'ajout de votre classe à nouveau à la société mère.

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

    // add a class to #dropzone

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

Vos événements encore le feu à plusieurs reprises, mais personne ne va le voir.

//Edit: Utiliser le dragmove événement pour écraser définitivement la dragleave événement:

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

    // add a class to #dropzone

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

Définir le dragleave événement uniquement pour la dropzone.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X