javascript - code - Arrastre de HTML5 activado al desplazar un elemento secundario
tags html5 (29)
El problema que tengo es que el evento dragleave
de un elemento se dragleave
cuando se dragleave
un elemento secundario de ese elemento. Además, dragenter
no se dispara cuando se dragenter
el elemento principal nuevamente hacia atrás.
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 el div
rojo caiga al arrastrar algo allí. Esto funciona, pero si arrastra hacia la p
niño, la dragleave
se dispara y el div
ya no es rojo. Volviendo a la caída div
también no vuelve rojo. Es necesario salir completamente del div
y volver a arrastrarlo para que quede rojo.
¿Es posible evitar que la dragleave
de arrastre se dispare al arrastrar a un elemento secundario?
Actualización 2017: TL; DR, Buscar pointer-events: none;
CSS pointer-events: none;
como se describe en la respuesta de @ HD a continuación que funciona en los navegadores modernos e IE11.
¿Es posible evitar que la hoja de arrastre se dispare al arrastrar a un elemento secundario?
Sí.
#drop * {pointer-events: none;}
Ese CSS parece ser suficiente para Chrome.
Mientras lo usa con Firefox, el #drop no debería tener nodos de texto directamente (de lo contrario, existe un problema extraño en el que un elemento "se lo deja solo" ), por lo que sugiero que lo deje con un solo elemento (por ejemplo, use un div en el interior). # drop para poner todo dentro
Aquí hay un jsfiddle resolviendo el http://jsfiddle.net/pimvdb/HU6Mk/1/ la http://jsfiddle.net/pimvdb/HU6Mk/1/ .
También he hecho una versión simplificada derivada del ejemplo de @Theodore Brown, pero basada solo 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 estos pointer-events: none;
varias veces, sin embargo, es probable que se utilice junto con caídas de degradación elegante. Al menos es tan simple y resuelve el problema para muchos entornos.
Aquí hay otra solución utilizando 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'');
}
}
Espero que esto funcione, aquí hay un fiddle .
Aquí, la solución de navegador cruzado más simple (en serio):
jsfiddle <- intenta arrastrar algún archivo dentro de la caja
Puedes hacer algo así:
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, creará una "máscara" dentro de la zona de caída, con ancho y altura heredados, posición absoluta, que solo se mostrará cuando comience el dragover.
Entonces, después de mostrar esa máscara, puedes hacer el truco adjuntando los otros eventos de arrastrar y soltar en ella.
Después de salir o soltar, simplemente ocultas la máscara de nuevo.
Sencilla y sin complicaciones.
(Obs .: consejo de Greg Pettit: debe asegurarse de que la máscara se mueva por toda la caja, incluido el borde)
Después de pasar tantas horas conseguí esa sugerencia trabajando exactamente como estaba previsto. Quería proporcionar una referencia solo cuando los archivos fueron arrastrados y el documento de Dragover, dragleave estaba causando parpadeos dolorosos en el navegador Chrome.
Así es como lo resolví, también lanzando las señales adecuadas para el usuario.
$(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
});
El problema es que el evento dragleave
se dispara cuando el mouse se dragleave
frente al elemento hijo.
He intentado varios métodos de verificación para ver si el elemento e.target
es el mismo que this
elemento, pero no pude obtener ninguna mejora.
La forma en que solucioné este problema fue un poco hack, pero funciona al 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'');
}
}
Escribí un módulo de arrastrar y soltar llamado drip-drop que corrige este extraño comportamiento, entre otros. Si está buscando un buen módulo de arrastrar y soltar de bajo nivel que pueda usar como base para cualquier cosa (carga de archivos, arrastrar y soltar en la aplicación, arrastrar desde o hacia fuentes externas), debe verificar esto módulo de salida:
Así es como harías lo que intentas hacer en la gota:
$(''#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'')
}
})
})
Para hacer esto sin una biblioteca, la técnica de contador es lo que usé en la gota de goteo, aunque la respuesta con la calificación más alta omite pasos importantes que harán que las cosas se rompan para todo, excepto la primera gota. Aquí está cómo hacerlo correctamente:
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
}
});
Ha pasado bastante tiempo desde que se hizo esta pregunta y se proporcionaron muchas soluciones (incluyendo hacks feos).
Logré solucionar el mismo problema que tuve recientemente gracias a la respuesta en esta answer y pensé que podría ser útil para alguien que llega a esta página. La idea es almacenar el evenet.target
en ondrageenter
cada vez que se llama en cualquiera de los elementos padre o hijo. Luego, en ondragleave
compruebe si el objetivo actual ( event.target
) es igual al objeto que almacenó en ondragenter
.
El único caso en que estos dos coinciden es cuando su arrastre abandona la ventana del navegador.
La razón por la que esto funciona bien es cuando el mouse abandona un elemento (por ejemplo, el1
) y entra en otro elemento (por ejemplo, el2
), primero se llama el2.ondragleave
y luego el2.ondragleave
. Solo cuando la función de arrastrar está saliendo / ingresando a la ventana del navegador, event.target
estará ''''
tanto en el1.ondragenter
como en el2.ondragleave
.
Aquí está mi muestra de trabajo. Lo he probado 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 el estilo adecuado, lo que he hecho es hacer que la división interna (# file-drop-area) sea mucho más grande cada vez que se arrastra un archivo a la pantalla para que el usuario pueda colocar fácilmente los archivos en el lugar adecuado.
He escrito una pequeña biblioteca llamada Dragster para manejar este problema exacto, funciona en todas partes, excepto en no hacer nada en IE (que no es compatible con DOM Event Constructors, pero sería bastante fácil escribir algo similar utilizando los eventos personalizados de jQuery)
La forma "correcta" de resolver este problema es deshabilitar los eventos de puntero en elementos secundarios del destino de caída (como en la respuesta de @ HD). Aquí hay un jsFiddle que creé que demuestra esta técnica . Desafortunadamente, esto no funciona en versiones de Internet Explorer anteriores a IE11, ya que http://caniuse.com/pointer-events .
Afortunadamente, pude encontrar una solución que funciona en versiones antiguas de IE. Básicamente, implica identificar e ignorar los eventos de dragleave
que se producen al arrastrar elementos secundarios. Debido a que el evento dragenter
se dragenter
en nodos secundarios antes del evento dragleave
en el padre, se pueden agregar detectores de eventos separados a cada nodo secundario que agrega o elimina una clase de "ignorar-arrastrar-dejar" del destino de colocación. Luego, el dragleave
eventos de dragleave
del objetivo de dragleave
puede simplemente ignorar las llamadas que ocurren cuando existe esta clase. Aquí hay un jsFiddle que demuestra esta solución . Está probado y funciona en Chrome, Firefox e IE8 +.
Actualizar:
Creé un jsFiddle que demuestra una solución combinada que usa detección de características, donde se usan eventos de puntero si son compatibles (actualmente Chrome, Firefox e IE11), y el navegador recurre a agregar eventos a nodos secundarios si el soporte de eventos de puntero no está disponible (IE8 -10).
Me he topado con el mismo problema y aquí está mi solución, que creo que es mucho más fácil que la anterior. No estoy seguro de si se trata de un navegador cruzado (podría depender incluso de un orden de propagación)
Usaré jQuery por simplicidad, pero la solución debería ser independiente del marco.
El evento burbujea para los padres de cualquier manera, así dado:
<div class="parent">Parent <span>Child</span></div>
Adjuntamos eventos
el = $(''.parent'')
setHover = function(){ el.addClass(''hovered'') }
onEnter = function(){ setTimeout(setHover, 1) }
onLeave = function(){ el.removeClass(''hovered'') }
$(''.parent'').bind(''dragenter'', onEnter).bind(''dragleave'', onLeave)
Y eso es todo. :) funciona porque a pesar de que onEnter en el niño se dispara antes de onLeave en el padre, lo demoramos un poco invirtiendo el orden, por lo que la clase se elimina primero y luego se vuelve a aplicar después de un milisegundo.
No estoy seguro de si este navegador cruzado, pero probé en Chrome y resuelve mi problema:
Quiero arrastrar y soltar un archivo en toda la página, pero mi hoja de arrastre se activa cuando arrastro sobre el elemento secundario. Mi solución fue mirar la xey del mouse:
Tengo un div que se superpone a toda mi página, cuando la página se carga, lo oculto.
cuando arrastra sobre el documento, lo muestro, y cuando lo coloca en el padre, lo maneja, y cuando deja el padre, verifico xey.
$(''#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;
});
Puedes arreglarlo en Firefox con un poco de inspiración del código fuente de 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'');
}
}
}
Desafortunadamente, no funciona en Chrome porque relatedTarget
parece no existir en eventos de dragleave
, y asumo que estás trabajando en Chrome porque tu ejemplo no funcionó en Firefox. Aquí hay una versión con el código anterior implementado.
Si está utilizando HTML5, puede obtener el clientRect del padre:
let rect = document.getElementById("drag").getBoundingClientRect();
Luego en el parent.dragleave ():
dragleave(e) {
if(e.clientY < rect.top || e.clientY >= rect.bottom || e.clientX < rect.left || e.clientX >= rect.right) {
//real leave
}
}
Solo necesita mantener un contador de referencia, incrementarlo cuando obtenga un dragenter, disminuir cuando obtenga un dragleave. Cuando el contador está en 0 - eliminar 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 evento drop, reinicie el contador a cero y borre la clase agregada.
Puedes ejecutarlo here
Solo verifique si el elemento arrastrado es un niño, si lo es, entonces no elimine su clase de estilo ''dragover''. Bastante simple y funciona para mi:
$yourElement.on(''dragleave dragend drop'', function(e) {
if(!$yourElement.has(e.target).length){
$yourElement.removeClass(''is-dragover'');
}
})
Tenía el mismo problema y traté de usar la solución pk7s. Funcionó, pero podría hacerse un poco mejor sin ningún elemento adicional de dom.
Básicamente, la idea es la misma: agregue una superposición extra invisible sobre el área de colocación. Solo hagamos esto sin elementos dom adicionales. Aquí está la parte donde los pseudo-elementos de CSS vienen a jugar.
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
Esta regla posterior creará una superposición totalmente cubierta para el área desplegable.
#box.overlay:after {
content:'''';
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
z-index: 1;
}
Aquí está la solución completa: http://jsfiddle.net/F6GDq/8/
Espero que ayude a cualquiera con el mismo problema.
Una solución de trabajo alternativa, un poco más 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) {
Una solución muy simple es usar la propiedad CSS de pointer-events
. Simplemente establezca su valor en none
sobre Dragstart en cada elemento secundario. Estos elementos ya no activarán eventos relacionados con el mouse, por lo que no atraparán el mouse sobre ellos y, por lo tanto, no activarán el dragleave en el padre.
No olvide volver a establecer esta propiedad en auto
cuando termine de arrastrar;)
Una solución simple es agregar los pointer-events: none
regla css pointer-events: none
al componente secundario para evitar el desencadenamiento de ondragleave
. Ver ejemplo:
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 not triggering dragleave</p>
</div>
Y aquí va, una solución para 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!'');
}
})
Aquí hay otro enfoque basado en el tiempo de los eventos.
El dragenter
evento enviado desde el elemento secundario puede ser capturado por el elemento principal y siempre ocurre antes de dragleave
. El tiempo entre estos dos eventos es realmente corto, más corto que cualquier posible acción del mouse humano. Entonces, la idea es memorizar el momento en que dragenter
ocurre un dragleave
evento y filtrar los eventos que ocurren "no demasiado rápido" después de ...
Este breve ejemplo funciona en Chrome y 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
}
})
Debe eliminar los eventos de puntero para todos los objetos secundarios del destino de arrastre.
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) {
//
}
}
}
Encontré una solución similar pero más elegante a la respuesta de @ azlar, y esta es mi solución:
$(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();
}
});
Este método detecta si el ratón ha salido de la página. Funciona bien en Chrome y Edge.
Tuve un problema similar: mi código para ocultar la zona de arrastre en el evento de dragleave para el cuerpo se disparó de forma contatente cuando se movían los elementos secundarios haciendo que la zona de caída parpadee en Google Chrome.
Pude resolver esto programando la función para ocultar Dropzone en lugar de llamarlo de inmediato. Luego, si se dispara otro dragover o arrastre, la función programada se cancela.
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);
pimvdb ..
¿Por qué no intentas usar drop en lugar de dragleave ? Funcionó para mí. Espero que esto resuelva tu problema.
Por favor, consulte el archivo 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");
}
});
El evento " dragleave " se activa cuando el puntero del mouse sale del área de arrastre del contenedor de destino.
Lo que tiene mucho sentido, ya que en muchos casos solo el padre puede ser dropable y no los descendientes. Creo que event.stopPropogation () debería haber manejado este caso, pero parece que no funciona.
Las soluciones mencionadas anteriormente parecen funcionar para la mayoría de los casos, pero fallan en el caso de aquellos niños que no admiten eventos de dragenter / dragleave , como iframe .
Una solución alternativa es comprobar el evento.relatedTarget y verificar si reside dentro del contenedor y luego ignorar el evento dragleave como lo he hecho aquí:
function isAncestor(node, target) {
if (node === target) return false;
while(node.parentNode) {
if (node.parentNode === target)
return true;
node=node.parentNode;
}
return false;
}
var container = document.getElementById("dropbox");
container.addEventListener("dragenter", function() {
container.classList.add("dragging");
});
container.addEventListener("dragleave", function(e) {
if (!isAncestor(e.relatedTarget, container))
container.classList.remove("dragging");
});
¡Puedes encontrar un violín de trabajo here !
Puede usar un tiempo de espera con una transitioning
bandera y escuchar en el elemento superior. dragenter
/ A dragleave
partir de los eventos infantiles se acumularán en el contenedor.
Ya que dragenter
en el elemento secundario se dispara antes dragleave
del contenedor, estableceremos la visualización de la bandera como transición durante 1 ms ... el dragleave
oyente comprobará la bandera antes de que finalice 1 ms.
La bandera se cumplirá solo durante las transiciones a elementos secundarios y no se cumplirá cuando se realice la transición a un elemento principal (del contenedor)
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/
Solo trata de usar el event.eventPhase. Se establecerá en 2 (Evento.AT_TARGET) solo si se ingresó el objetivo, de lo contrario, se configurará en 3 (Evento.BUBBLING_PHASE).
He utilizado el EventPhase para enlazar o desvincular el evento 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
use este código 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'');
}
}
});