Dragleave HTML5 activado al pasar el cursor sobre un elemento secundario

Resuelto pimvdb asked hace 13 años • 0 respuestas

El problema que tengo es que el dragleaveevento de un elemento se activa al pasar el cursor sobre un elemento secundario de ese elemento. Además, dragenterno se activa al volver a colocar el cursor sobre el elemento principal.

Hice un violín simplificado: 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>

con el siguiente 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");
                 }
                });

Lo que se supone que debe hacer es notificar al usuario haciendo que la gota se ponga divroja cuando arrastra algo allí. Esto funciona, pero si lo arrastras hacia el pniño, dragleavese dispara y divya no está rojo. Volver a la gota divtampoco la vuelve roja nuevamente. Es necesario salir completamente de la caída divy arrastrarla nuevamente para que se vuelva roja.

¿Es posible evitar dragleaveque se dispare al arrastrar a un elemento secundario?

Actualización de 2017: TL; DR, busque CSS pointer-events: none;como se describe en la respuesta de @HD a continuación que funciona en navegadores modernos e IE11.

pimvdb avatar Aug 18 '11 22:08 pimvdb
Aceptado

Sólo necesita mantener un contador de referencia, incrementarlo cuando obtenga un dragenter, disminuirlo cuando obtenga un dragleave. Cuando el contador esté en 0, elimine la clase.

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

Nota: En el caso de caída, restablezca el contador a cero y borre la clase agregada.

Puedes ejecutarlo aquí

Woody avatar Jan 08 '2014 17:01 Woody

¿Es posible evitar que se active dragleave al arrastrar a un elemento secundario?

Sí.

#drop * {pointer-events: none;}

Ese CSS parece ser suficiente para Chrome.

Al usarlo con Firefox, #drop no debería tener nodos de texto directamente (de lo contrario, hay un problema extraño cuando un elemento "lo deja solo" ), por lo que sugiero dejarlo con un solo elemento (por ejemplo, use un div dentro #drop para meter todo dentro)

Aquí hay un ejemplo de jsfiddle que resuelve la pregunta original (rota) .

También hice una versión simplificada bifurcada del ejemplo de @Theodore Brown, pero basada únicamente en este CSS.

Sin embargo, no todos los navegadores tienen implementado este CSS: http://caniuse.com/pointer-events

Al ver el código fuente de Facebook, pude encontrar esto pointer-events: none;varias veces; sin embargo, probablemente se use junto con elegantes alternativas de degradación. Al menos es tan simple y resuelve el problema en muchos entornos.

H.D. avatar Sep 03 '2013 01:09 H.D.

Ha pasado bastante tiempo desde que se hizo esta pregunta y se proporcionaron muchas soluciones (incluidos trucos feos).

Logré solucionar el mismo problema que tuve recientemente gracias a la respuesta en esta respuesta y pensé que podría ser útil para alguien que visite esta página. La idea es almacenar el evenet.targetin ondrageentercada vez que se llama a cualquiera de los elementos padre o hijo. Luego, ondragleaveverifique si el objetivo actual ( event.target) es igual al objeto que almacenó ondragenter.

El único caso en el que estos dos coinciden es cuando al arrastrar sale de la ventana del navegador.

La razón por la que esto funciona bien es cuando el mouse sale de un elemento (por ejemplo el1) y ingresa a otro elemento (por ejemplo el2), primero se el2.ondragenterllama y luego el1.ondragleave. Solo cuando el arrastre salga/ingrese de la ventana del navegador, event.targetestará ''en ambos el2.ondragenter y el1.ondragleave.

Aquí está mi muestra de trabajo. Lo probé en IE9+, Chrome, Firefox y 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;
    };
})();

Y aquí hay una página 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>

Con un estilo adecuado, lo que he hecho es hacer que el div interno (#file-drop-area) sea mucho más grande cada vez que se arrastra un archivo a la pantalla para que el usuario pueda colocar los archivos fácilmente en el lugar adecuado.

Ali Motevallian avatar Oct 20 '2014 05:10 Ali Motevallian

Aquí, la solución multinavegador más sencilla (en serio):

jsfiddle <-- intente arrastrar algún archivo dentro del cuadro

Puedes hacer algo como eso:

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 pocas palabras, crea una "máscara" dentro de la zona de colocación, con ancho y alto heredados, posición absoluta, que solo se mostrará cuando comience el arrastre.
Entonces, después de mostrar esa máscara, puedes hacer el truco adjuntando los demás eventos de arrastrar y soltar en ella.

Después de salir o dejarse caer, simplemente vuelve a ocultar la máscara.
Sencillo y sin complicaciones.

(Obs.: consejo de Greg Pettit: debe asegurarse de que la máscara cubra todo el cuadro, incluido el borde)

Diego T. Yamaguchi avatar May 06 '2013 17:05 Diego T. Yamaguchi