El problema que tengo es que el dragleave
evento de un elemento se dispara cuando se pasa el elemento secundario de ese elemento. Además, dragenter
no se activa cuando se vuelve a desplazar el elemento primario.
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 sea div
roja al arrastrar algo allí. Esto funciona, pero si arrastra al p
niño, dragleave
se dispara y div
ya no es rojo. Volver a la gota div
tampoco lo vuelve rojo. Es necesario salir completamente de la gota div
y arrastrarla nuevamente para que quede roja.
¿Es posible evitar dragleave
disparar al arrastrar a un elemento secundario?
Actualización 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.
dragleave
todavía se dispara en ese caso.Respuestas:
Solo necesita mantener un contador de referencia, incrementarlo cuando obtenga una gragera, disminuir cuando obtenga un dragleave. Cuando el contador está en 0, elimine la clase.
Nota: en el caso de caída, restablezca el contador a cero y borre la clase agregada.
Puedes ejecutarlo aquí
fuente
pointer-events: none
los niños. Cuando empiezo a arrastrar, agrego una clase que tiene esta propiedad, cuando se arrastra, elimino la clase. Funcionó muy bien en Safari y Chrome, pero no en Firefox.dragenter
guardoevent.currentTarget
en una nueva variabledragEnterTarget
. MientrasdragEnterTarget
esté configurado, ignoro otrosdragenter
eventos, porque son de niños. En todos losdragleave
eventos lo verificodragEnterTarget === event.target
. Si esto es falso, el evento será ignorado ya que fue disparado por un niño. Si esto es cierto me reiniciodragEnterTarget
aundefined
.Si.
Ese CSS parece ser suficiente para Chrome.
Al usarlo con Firefox, el #drop no debería tener nodos de texto directamente (de lo contrario, hay un problema extraño en el que un elemento "lo deja solo" ), por lo que sugiero dejarlo con un solo elemento (por ejemplo, use un div dentro #drop para poner todo dentro)
Aquí hay un jsfiddle para resolver el ejemplo de la pregunta original (rota) .
También hice una versión simplificada bifurcada del ejemplo @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 esto
pointer-events: none;
varias veces, sin embargo, probablemente se use junto con fallas de degradación elegantes. Al menos es tan simple y resuelve el problema para muchos entornos.fuente
Aquí, la solución más simple de Cross-Browser (en serio):
jsfiddle <- intenta arrastrar algún archivo dentro del cuadro
Puedes hacer algo así:
En pocas palabras, crea una "máscara" dentro de la zona de caída, con ancho y alto 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 dragleave & drop en ella.
Después de irse o dejarse caer, simplemente oculta la máscara nuevamente.
Simple y sin complicaciones.
(Obs .: consejos de Greg Pettit: debe asegurarse de que la máscara se desplace por toda la caja, incluido el borde)
fuente
Ha pasado bastante tiempo después de que se haga esta pregunta y se proporcionan muchas soluciones (incluidos los hacks 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 llega a esta página. La idea es almacenar el
evenet.target
enondrageenter
cada se le llama en cualquiera de los elementos principales o secundarios. Luego,ondragleave
verifique si el objetivo actual (event.target
) es igual al objeto que almacenóondragenter
.El único caso en que estos dos coinciden es cuando su arrastre sale de la ventana del navegador.
La razón por la que esto funciona bien es cuando el mouse deja un elemento (digamos
el1
) y entra en otro elemento (digamosel2
), primeroel2.ondragenter
se llama y luegoel1.ondragleave
. Solo cuando el arrastre salga / ingrese a la ventana del navegador,event.target
estará''
en ambosel2.ondragenter
yel1.ondragleave
.Aquí está mi muestra de trabajo. Lo he probado en IE9 +, Chrome, Firefox y Safari.
Y aquí hay una página html simple:
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 soltar fácilmente los archivos en el lugar adecuado.
fuente
==
over===
. Está comparando las referencias de objeto de destino del evento, por lo que también===
funciona bien.La forma "correcta" de resolver este problema es deshabilitar los eventos de puntero en elementos secundarios del destino de colocación (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 no admitían eventos de puntero .
Por suerte, yo era capaz de llegar a una solución, que hace el trabajo en las versiones antiguas de IE. Básicamente, implica identificar e ignorar
dragleave
eventos que ocurren al arrastrar elementos secundarios. Debido a que eldragenter
evento se dispara en los nodos secundarios antes deldragleave
evento en el primario, se pueden agregar escuchas de eventos separados a cada nodo secundario que agregan o eliminan una clase "ignorar-arrastrar-dejar" del destino de colocación. Entonces, eldragleave
oyente de eventos del destino puede simplemente ignorar las llamadas que ocurren cuando esta clase existe. Aquí hay un jsFiddle que demuestra esta solución . Está probado y funciona en Chrome, Firefox e IE8 +.Actualizar:
He creado un jsFiddle demostrando una solución combinada mediante la detección de características, donde se utilizan los eventos de puntero si es compatible (actualmente Chrome, Firefox y IE11), y el navegador vuelve a caer a añadir eventos a los nodos secundarios si el soporte evento puntero no está disponible (IE8 -10).
fuente
dragleave
evento se active en Chrome, Firefox e IE11 +. También actualicé mi otra solución para admitir IE8 e IE9, además de IE10. Es intencional que el efecto dropzone solo se agregue al arrastrar el enlace "Arrastrarme". Otros pueden sentirse libres de cambiar este comportamiento según sea necesario para respaldar sus casos de uso.El problema es que el
dragleave
evento se desencadena cuando el mouse se coloca delante del elemento secundario.He intentado varios métodos de verificación para ver si el
e.target
elemento es el mismo que elthis
elemento, pero no pude obtener ninguna mejora.La forma en que solucioné este problema fue un poco pirata, pero funciona al 100%.
fuente
if (e.x >= (rect.left + rect.width) || e.x <= rect.left || e.y >= (rect.top + rect.height) || e.y <= rect.top)
e.x
ye.y
.Si está utilizando HTML5, puede obtener el cliente del padre.
Luego, en parent.dragleave ():
aquí hay un jsfiddle
fuente
border-radius
, mover el puntero cerca de la esquina en realidad dejaría el elemento, pero este código aún pensaría que estamos dentro (hemos dejado el elemento pero todavía estamos en el rectángulo delimitador). Entonces eldragleave
controlador de eventos no se llamará en absoluto.Una solución muy simple es usar la
pointer-events
propiedad CSS . Simplemente establezca su valor ennone
en 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
auto
al finalizar el arrastre;)fuente
Esta solución bastante simple me está funcionando hasta ahora, suponiendo que su evento se adjunte a cada elemento de arrastre individualmente.
fuente
Solución muy simple:
fuente
Puedes arreglarlo en Firefox con un poco de inspiración del código fuente de jQuery :
Desafortunadamente, no funciona en Chrome porque
relatedTarget
parece no existir en losdragleave
eventos, y supongo que está trabajando en Chrome porque su ejemplo no funcionó en Firefox. Aquí hay una versión con el código anterior implementado.fuente
Y aquí va, una solución para Chrome:
fuente
border-radius
como expliqué en mi comentario sobre la respuesta de @ azlar anterior.Aquí hay otra solución usando document.elementFromPoint:
Espero que esto funcione, aquí hay un violín .
fuente
event.clientX, event.clientY
en su lugar, ya que son relativos a la ventana gráfica y no a la página. Espero que eso ayude a otra alma perdida por ahí.Una solución simple es agregar la regla css
pointer-events: none
al componente hijo para evitar el desencadenante deondragleave
. Ver ejemplo:fuente
dragged = event.target;clone = dragged.cloneNode();clone.style.pointerEvents = 'none';
No estoy seguro de si este navegador cruzado, pero lo probé en Chrome y resuelve mi problema:
Quiero arrastrar y soltar un archivo en toda la página, pero mi dragleave se activa cuando arrastro sobre el elemento secundario. Mi solución fue mirar la x e y 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 suelta sobre el padre, lo maneja, y cuando sale del padre, verifico x e y.
fuente
He escrito una pequeña biblioteca llamada Dragster para manejar este problema exacto, funciona en todas partes excepto silenciosamente sin hacer nada en IE (que no admite DOM Event Constructors, pero sería bastante fácil escribir algo similar usando los eventos personalizados de jQuery)
fuente
Estaba teniendo el mismo problema e intenté usar la solución pk7s. Funcionó, pero podría hacerse un poco mejor sin ningún elemento dom adicional.
Básicamente, la idea es la misma: agregue una superposición no visible adicional sobre el área desplegable. Solo hagamos esto sin ningún elemento dom adicional. Aquí está la parte donde los pseudo-elementos CSS vienen a jugar.
Javascript
CSS
Esta regla posterior creará una superposición completamente cubierta para el área desplegable.
Aquí está la solución completa: http://jsfiddle.net/F6GDq/8/
Espero que ayude a cualquiera con el mismo problema.
fuente
Simplemente verifique si el elemento arrastrado es un elemento secundario, si lo es, no elimine su clase de estilo 'dragover'. Bastante simple y funciona para mí:
fuente
Me he encontrado 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 si es crossbrowser (puede depender incluso del orden de burbujeo)
Usaré jQuery por simplicidad, pero la solución debe ser independiente del marco.
El evento burbujea a los padres de cualquier manera, dado:
Adjuntamos eventos
Y eso es todo. :) funciona porque aunque onEnter en los incendios secundarios antes que onLeave en los padres, lo retrasamos un poco invirtiendo el orden, por lo que la clase se elimina primero y luego se vuelve a aplicar después de un milisegundo.
fuente
Escribí un módulo de arrastrar y soltar llamado drip-drop que corrige este comportamiento extraño, 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 fuera:
https://github.com/fresheneesz/drip-drop
Así es como haría lo que intenta hacer en goteo:
Para hacer esto sin una biblioteca, la técnica de contador es la que utilicé en el goteo, aunque la respuesta mejor calificada omite pasos importantes que harán que las cosas se rompan para todo excepto la primera caída. Aquí se explica cómo hacerlo correctamente:
fuente
Una solución de trabajo alternativa, un poco más simple.
fuente
clientX
más de 0 cuando el mouse está fuera de la caja. De acuerdo, mis elementos sonposition:absolute
Después de pasar tantas horas, recibí esa sugerencia funcionando exactamente como estaba previsto. Quería proporcionar una señal solo cuando los archivos se arrastraban, y el arrastre de documentos, dragleave causaba parpadeos dolorosos en el navegador Chrome.
Así es como lo resolví, también arrojando pistas adecuadas para el usuario.
fuente
Tuve un problema similar: mi código para ocultar la zona de caída en el evento dragleave para el cuerpo se activó de forma contagiosa al pasar el cursor sobre elementos secundarios que hacen que la zona de caída parpadee en Google Chrome.
Pude resolver esto programando la función para ocultar la zona de caída en lugar de llamarla de inmediato. Luego, si se dispara otro dragover o dragleave, se cancela la llamada de función programada.
fuente
El evento " dragleave " se activa cuando el puntero del mouse sale del área de arrastre del contenedor de destino.
Lo cual tiene mucho sentido ya que en muchos casos solo el padre puede ser descartable y no los descendientes. Creo que event.stopPropogation () debería haber manejado este caso, pero parece que no funciona.
Anteriormente, algunas soluciones parecen funcionar para la mayoría de los casos, pero falla en el caso de aquellos niños que no admiten eventos de arrastre / dragleave , como iframe .
1 solución es verificar el event.relatedTarget y verificar si reside dentro del contenedor, luego ignore el evento dragleave como lo he hecho aquí:
Puedes encontrar un violín que funcione aquí !
fuente
Resuelto ..!
Declare cualquier matriz para ex:
No hay que preocuparse por los elementos secundarios.
fuente
Luché MUCHO con esto, incluso después de leer todas estas respuestas, y pensé que podría compartir mi solución con ustedes, porque pensé que podría ser uno de los enfoques más simples, aunque algo diferente. Mi pensamiento era simplemente omitir el
dragleave
completo el detector de eventos y codificar el comportamiento de dragleave con cada nuevo evento dragenter activado, mientras me aseguraba de que los eventos dragenter no se activaran innecesariamente.En mi ejemplo a continuación, tengo una tabla, donde quiero poder intercambiar el contenido de las filas de la tabla entre sí a través de la API de arrastrar y soltar. Activado
dragenter
, se agregará una clase CSS al elemento de fila al que está arrastrando actualmente su elemento, para resaltarlo, y activado ,dragleave
se eliminará esta clase.Ejemplo:
Tabla HTML muy básica:
Y la función de controlador de eventos dragenter, añadido en cada celda de la tabla (a un lado
dragstart
,dragover
,drop
, ydragend
los manipuladores, los cuales no son específicos de esta cuestión, por lo que no copiados aquí):Se dejaron comentarios muy básicos en el código, de modo que este código también es adecuado para principiantes. ¡Espero que esto te ayude! Tenga en cuenta que, por supuesto, deberá agregar todos los oyentes de eventos que mencioné anteriormente en cada celda de la tabla para que esto funcione.
fuente
Aquí hay otro enfoque basado en el momento de los eventos.
El
dragenter
elemento principal puede capturar el evento enviado desde el elemento secundario y siempre se produce antes deldragleave
. El tiempo entre estos dos eventos es realmente corto, más corto que cualquier acción posible del ratón humano. Entonces, la idea es memorizar el momento en quedragenter
sucede y filtrardragleave
eventos que ocurren "no demasiado rápido" después de ...Este breve ejemplo funciona en Chrome y Firefox:
fuente
pimvdb ..
¿Por qué no pruebas usando drop en lugar de dragleave ? Funcionó para mi. Espero que esto resuelva tu problema.
Por favor revise el jsFiddle: http://jsfiddle.net/HU6Mk/118/
fuente
Puede usar un tiempo de espera con una
transitioning
bandera y escuchar en el elemento superior.dragenter
/dragleave
de los eventos secundarios aparecerán en el contenedor.Dado que
dragenter
en el elemento hijo se dispara antesdragleave
del contenedor, configuraremos el show de bandera como en transición durante 1 ms ... eldragleave
oyente verificará la bandera antes de que el 1ms esté activo.El indicador será verdadero solo durante las transiciones a elementos secundarios, y no será verdadero al pasar a un elemento primario (del contenedor)
jsfiddle: http://jsfiddle.net/ilovett/U7mJj/
fuente
Debe eliminar los eventos de puntero para todos los objetos secundarios del objetivo de arrastre.
fuente