javascript - quantum - Cómo detectar el evento dragleave en Firefox cuando se arrastra fuera de la ventana
drag and drop javascript ejemplo (5)
Dependiendo de lo que desee lograr, puede evitar este problema utilizando la :-moz-drag-over
que solo está disponible en Firefox y le permite reaccionar ante un archivo que se arrastra sobre un elemento.
Echa un vistazo a esta demo simple http://codepen.io/ryanseddon/pen/Ccsua
.dragover {
background: red;
width: 500px;
height: 300px;
}
.dragover:-moz-drag-over {
background: green;
}
Firefox no activa correctamente el evento dragleave cuando se arrastra fuera de la ventana:
https://bugzilla.mozilla.org/show_bug.cgi?id=665704
https://bugzilla.mozilla.org/show_bug.cgi?id=656164
Estoy intentando desarrollar una solución alternativa para esto (que sé que es posible porque Gmail lo está haciendo), pero lo único que se me ocurre parece realmente hackish.
Una forma de saber cuándo se arrastra fuera de la ventana es esperar que el evento de dragover
deje de disparar (porque la dragover
dispara constantemente durante una operación de arrastrar y soltar). Así es como estoy haciendo eso:
var timeout;
function dragleaveFunctionality() {
// do stuff
}
function firefoxTimeoutHack() {
clearTimeout(timeout);
timeout = setTimeout(dragleaveFunctionality, 200);
}
$(document).on(''dragover'', firefoxTimeoutHack);
Este código esencialmente crea y borra un tiempo de espera una y otra vez. El tiempo de espera de 200 milisegundos no se alcanzará a menos que el evento de dragover
deje de disparar.
Mientras esto funciona, no me gusta la idea de usar un tiempo de espera para este propósito. Se siente mal También significa que hay un ligero retraso antes de que desaparezca el estilo de "dropzone".
La otra idea que tuve fue detectar cuándo el mouse deja la ventana, pero las formas normales de hacerlo no parecen funcionar durante las operaciones de arrastrar y soltar.
¿Alguien tiene una mejor manera de hacer esto?
ACTUALIZAR:
Aquí está el código que estoy usando:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Drag and Drop Issue</title>
<script src="http://code.jquery.com/jquery.js"></script>
</head>
<body>
Open up the console and look at what number is reporting when dragging files in and out of the window. The number should always be 0 when leaving the window, but in Firefox it''s not.
<script type="text/javascript">
$(function() {
var counter = 0;
$(document).on(''dragenter'', function(e) {
counter += 1;
console.log(counter, e.target);
});
$(document).on(''dragleave'', function(e) {
counter -= 1;
console.log(counter, e.target);
});
});
</script>
</body>
</html>
Encontré una solución. El problema no era tanto que el evento de dragleave
no estuviese en dragleave
; más bien, el evento dragenter
se disparaba dos veces al arrastrar primero un archivo a la ventana (y, a veces, al arrastrar ciertos elementos). Mi solución original fue usar un contador para rastrear cuando se dragleave
evento dragleave
final, pero el doble disparo de eventos dragenter
estaba estropeando el conteo. (¿Por qué no podría simplemente escuchar para dragleave
preguntas? Bueno, porque dragleave
funciona de manera muy similar a mouseout
en que se dispara no solo al salir del elemento sino también al ingresar un elemento hijo. Por lo tanto, cuando dragleave
dispara, su ratón puede muy bien aún estar dentro de los límites del elemento original.)
La solución que se me ocurrió fue hacer un seguimiento de los elementos que dragenter
y dragleave
habían sido activados. Dado que los eventos se propagan hasta el documento, escuchar dragenter
y dragleave
en un elemento particular capturará no solo eventos en ese elemento sino también eventos en sus elementos dragleave
.
Entonces, creé una jQuery collection $()
para realizar un seguimiento de qué eventos se activaron en qué elementos. event.target
el event.target
a la colección cada vez que se disparaba dragenter, y event.target
de la colección cada vez que ocurría dragleave. La idea era que si la colección estaba vacía significaría que realmente había abandonado el elemento original porque si en lugar de eso ingresara un elemento hijo, al menos un elemento (el elemento secundario) seguiría en la colección jQuery. Por último, cuando se drop
evento drop
, quiero restablecer la colección para vaciar, por lo que está listo para ir cuando se produce el próximo evento dragenter
.
jQuery también ahorra mucho trabajo extra porque duplica automáticamente las comprobaciones, por lo que event.target
no se agrega dos veces, incluso cuando Firefox estaba invocando dragenter
.
Uf, de todos modos, aquí hay una versión básica del código que terminé usando. Lo puse en un plugin jQuery simple si alguien más está interesado en usarlo. Básicamente, llamas a .draghover
en cualquier elemento, y draghoverstart
se activa al arrastrar primero el elemento, y draghoverend
se activa una vez que el arrastre realmente lo ha dejado.
// The plugin code
$.fn.draghover = function(options) {
return this.each(function() {
var collection = $(),
self = $(this);
self.on(''dragenter'', function(e) {
if (collection.length === 0) {
self.trigger(''draghoverstart'');
}
collection = collection.add(e.target);
});
self.on(''dragleave drop'', function(e) {
collection = collection.not(e.target);
if (collection.length === 0) {
self.trigger(''draghoverend'');
}
});
});
};
// Now that we have a plugin, we can listen for the new events
$(window).draghover().on({
''draghoverstart'': function() {
console.log(''A file has been dragged into the window.'');
},
''draghoverend'': function() {
console.log(''A file has been dragged out of window.'');
}
});
Inspirado por el código de @PhilipWalton, simplifiqué el código del complemento jQuery.
$.fn.draghover = function(fnIn, fnOut) {
return this.each(function() {
var n = 0;
$(this).on(''dragenter'', function(e) {
(++n, n==1) && fnIn && fnIn.call(this, e);
}).on(''dragleave drop'', function(e) {
(--n, n==0) && fnOut && fnOut.call(this, e);
});
});
};
Ahora puede usar el plugin jquery como el método jquery hover:
// Testing code 1
$(window).draghover(function() {
console.log(''into window'');
}, function() {
console.log(''out of window'');
});
// Testing code 2
$(''#d1'').draghover(function() {
console.log(''into #d1'');
}, function() {
console.log(''out of #d1'');
});
¡La única solución que me ha funcionado y me ha llevado a algunos va con la esperanza de que esto ayude a alguien!
Tenga en cuenta que al realizar la clonación, necesita hacer un deepclone con eventos y datos:
HTML:
<div class="dropbox"><p>Child element still works!</p></div>
<div class="dropbox"></div>
<div class="dropbox"></div>
jQuery
$(''.dropbox'').each(function(idx, el){
$(this).data("counter" , 0);
});
$(''.dropbox'').clone(true,true).appendTo($(''body'');
$(''dropbox'').on({
dragenter : function(e){
$(this).data().counter++;
<!-- YOUR CODE HERE -->
},
dragleave: function(e){
$(this).data().counter--;
if($(this).data().counter === 0)
<!-- THEN RUN YOUR CODE HERE -->
}
});
addEvent(document, "mouseout", function(e) {
e = e ? e : window.event;
var from = e.relatedTarget || e.toElement;
if (!from || from.nodeName == "HTML") {
// stop your drag event here
// for now we can just use an alert
alert("left window");
}
});
Esto se copia de ¿Cómo puedo detectar cuándo el mouse sale de la ventana? . addEvent es simplemente crossbrowser addEventListener.