soltar example ejemplos ejemplo drop arrastrar archivos and jquery html5 javascript-events file-upload drag-and-drop

ejemplos - drag and drop jquery example



''dragleave'' de elemento principal se desencadena al arrastrar elementos secundarios (21)

Visión de conjunto

Tengo la siguiente estructura HTML y dragleave eventos dragenter y dragleave al elemento <div id="dropzone"> .

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

Problema

Cuando arrastro un archivo sobre <div id="dropzone"> , el evento dragenter se dragenter como se esperaba. Sin embargo, cuando muevo mi mouse sobre un elemento secundario, como <div id="drag-n-drop"> , el evento dragenter se dragenter para el elemento <div id="drag-n-drop"> y luego el dragleave evento dragleave se dragleave para el elemento <div id="dropzone"> .

Si vuelvo a pasar el elemento <div id="dropzone"> , el evento dragenter vuelve a dragenter , lo cual es genial, pero luego se dragleave evento dragleave para el elemento secundario que acaba de salir, por lo que se removeClass instrucción removeClass , que es no genial

Este comportamiento es problemático por 2 razones:

  1. Solo estoy adjuntando dragenter y dragleave a la <div id="dropzone"> así que no entiendo por qué los elementos hijos también tienen estos eventos adjuntos.

  2. Todavía estoy arrastrando el elemento <div id="dropzone"> al pasar el <div id="dropzone"> sobre sus hijos, así que no quiero que dragleave dispare.

jsFiddle

Aquí hay un jsFiddle para jugar: http://jsfiddle.net/yYF3S/2/

Pregunta

Entonces ... ¿cómo puedo hacer que cuando estoy arrastrando un archivo sobre el elemento <div id="dropzone"> , dragleave no se dispara incluso si estoy arrastrando elementos dragleave ... debería solo se dispara cuando dejo el elemento <div id="dropzone"> ... al pasar / arrastrar por cualquier lugar dentro de los límites del elemento no debe desencadenar el evento dragleave .

Necesito que sea compatible con todos los navegadores, al menos en los navegadores compatibles con HTML5 arrastrar y soltar, por lo que esta respuesta no es adecuada.

Parece que Google y Dropbox lo han descubierto, pero su código fuente es minificado / complejo, por lo que no he podido descifrarlo de su implementación.


@hristo tengo una solución mucho más elegante. Verifique si es algo que podría usar.

Tu esfuerzo no se desperdició después de todo. Pude usar el tuyo al principio, pero tuve problemas diferentes en FF, Chrome. Después de pasar tantas horas, obtuve esa sugerencia trabajando exactamente como estaba previsto.

Así es como es la implementación. También aproveché las indicaciones visuales para orientar correctamente a los usuarios sobre la zona de colocación.

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


Al principio, estuve de acuerdo con que las personas descartaran el enfoque pointer-events: none . Pero luego me pregunté a mí mismo:

¿Realmente necesita eventos de puntero para trabajar en los elementos secundarios mientras el arrastre está en progreso ?

En mi caso, tengo muchas cosas sucediendo en los niños, por ejemplo, colocar el mouse sobre los botones para acciones adicionales, edición en línea, etc. Sin embargo, nada de eso es necesario o incluso deseado durante un arrastre.

En mi caso, utilizo algo como esto para desactivar selectivamente los eventos de puntero para todos los nodos secundarios del contenedor padre:

div.drag-target-parent-container.dragging-in-progress * { pointer-events: none; }

Utilice su enfoque favorito para agregar / eliminar la clase dragging-in-progress en los controladores de eventos dragEnter / dragLeave , como hice o hago lo mismo en dragStart , et. Alabama.


Aquí, una de las soluciones más simples ● ︿ ●

Echa un vistazo a este fiddle <- intenta arrastrar algunos archivos dentro del cuadro

Puedes hacer algo como esto:

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

Por eso ya deberías saber qué está pasando aquí.
Solo mira el violín, ya sabes.


Como se mencionó en esta respuesta , puede evitar que los nodos secundarios disparen a los eventos, pero si necesita vincular algunos eventos, haga esto:

#dropzone.dragover *{ pointer-events: none; }

Y agregue este a su código JS:

$("#dropzone").on("dragover", function (event) { $("#dropzone").addClass("dragover"); }); $("#dropzone").on("dragleave", function (event) { $("#dropzone").removeClass("dragover"); });


De hecho, me gusta lo que veo en https://github.com/lolmaus/jquery.dragbetter/ pero quería compartir una posible alternativa. Mi estrategia general fue aplicar un estilo de fondo a la zona de caída (no a sus hijos) al dragearlo o cualquier elemento secundario (a través de burbujeo). Luego elimino el estilo cuando arrastro la zona de colocación. La idea era cuando se cambia a un niño, incluso si elimino el estilo de la zona de caída al abandonarlo (el dragleave se dispara), simplemente volvería a aplicar el estilo a la zona de colocación principal para dragear a cualquier niño. El problema es, por supuesto, que cuando se mueve de la zona de caída a un niño de la zona de caída, el dragenter se dispara contra el niño antes de la dragleave, por lo que mis estilos se aplicaron fuera de servicio. La solución para mí fue usar un temporizador para obligar al evento dragenter a regresar a la cola de mensajes, lo que me permitió procesarlo DESPUÉS de la dragleave. Usé un cierre para acceder al evento en la devolución de llamada del temporizador.

$(''.dropzone'').on(''dragenter'', function(event) { (function (event) { setTimeout(function () { $(event.target).closest(''.dropzone'').addClass(''highlight''); }, 0); }) (event.originalEvent); });

Esto parece funcionar en Chrome, es decir, Firefox y funciona independientemente del número de hijos en la zona de caída. Estoy un poco incómodo con el tiempo de espera que garantiza la resecuenciación de eventos, pero parece funcionar bastante bien para mi caso de uso.


El problema es que sus elementos dentro de las zonas de lanzamiento son, por supuesto, parte de la zona de caída y cuando ingresa los elementos secundarios, deja el elemento primario. Resolver esto no es fácil. También puede intentar agregar eventos a los niños y agregar su clase nuevamente al padre.

$("#dropzone,#dropzone *").on(''dragenter'', function(event) { // add a class to #dropzone event.stopPropagation(); // might not be necessary event.preventDefault(); return false; });

Tus eventos seguirán disparando varias veces pero nadie verá.

// Edit: use el evento dragmove para sobrescribir permanentemente el evento dragleave:

$("#dropzone,#dropzone *").on(''dragenter dragover'', function(event) { // add a class to #dropzone event.stopPropagation(); // might not be necessary event.preventDefault(); return false; });

Defina el evento dragleave solo para dropzone.


Entonces, para mí, el enfoque pointer-events: none; no funcionó muy bien ... Así que aquí está mi solución alternativa:

#dropzone { position: relative; } #dropzone(.active)::after { position: absolute; top: 0; right: 0; bottom: 0; left: 0; content: ''''; }

De esta manera, no es posible dragleave el elemento principal (en un elemento secundario) o dragover un elemento secundario. espero que esto ayude :)

* La clase ''activa'' que agrego cuando hago dragenter o dragleave . Pero si trabajas sin eso, simplemente abandona la clase.


Esta respuesta se puede encontrar aquí:

Dragleave HTML5 disparado cuando se desplaza un elemento secundario

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


Estaba tratando de implementar esto yo mismo, y no quería Jquery ni ningún complemento tampoco ... quería manejar la carga del archivo de la manera que consideraba mejor para mí ... así que esta es mi solución ... ¡y FUNCIONA! ! ...

Estructura de archivos

--- / uploads {uploads directory}

--- /js/slyupload.js {archivo de Javascript.}

--- index.php

--- upload.php

--- styles.css {solo un poco de estilo ..}


Ahora vamos a empezar, aquí está el código HTML ...

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <title>my Dzone Upload...</title> <link rel="stylesheet" href="styles.css" /> </head> <body> <div id="uploads"></div> <div class="dropzone" id="dropzone">Drop files here to upload</div> <script type="text/javascript" src="js/slyupload.js"></script> </body> </html>

Entonces este es el archivo Javascript adjunto :: ''js / slyupload.js''

<!-- begin snippet: --> <!-- language: lang-js --> // JavaScript Document //ondragover, ondragleave... (function(){ var dropzone = document.getElementById(''dropzone''); var intv; function displayUploads(data){ //console.log(data, data.length); var uploads = document.getElementById(''uploads''),anchor,x; for(x=0; x < data.length; x++){ //alert(data[x].file); anchor = document.createElement(''a''); anchor.href = data[x].file; anchor.innerText = data[x].name; uploads.appendChild(anchor); } } function upload(files){ //console.log(files); var formData = new FormData(), xhr = new XMLHttpRequest(), //for ajax calls... x; //for the loop.. for(x=0;x<files.length; x++){ formData.append(''file[]'', files[x]); /*//do this for every file... xhr = new XMLHttpRequest(); //open... and send individually.. xhr.open(''post'', ''upload.php''); xhr.send(formData);*/ } xhr.onload = function(){ var data = JSON.parse(this.responseText); //whatever comes from our php.. //console.log(data); displayUploads(data); //clear the interval when upload completes... clearInterval(intv); } xhr.onerror = function(){ console.log(xhr.status); } //use this to send all together.. and disable the xhr sending above... //open... and send individually.. intv = setInterval(updateProgress, 50); xhr.open(''post'', ''upload.php''); xhr.send(formData); //update progress... /* */ } function updateProgress(){ console.log(''hello''); } dropzone.ondrop = function(e){ e.preventDefault(); //prevent the default behaviour.. of displaying images when dropped... this.className = ''dropzone''; //we can now call the uploading... upload(e.dataTransfer.files); //the event has a data transfer object... } dropzone.ondragover = function(){ //console.log(''hello''); this.className = ''dropzone dragover''; return false; } dropzone.ondragleave = function(){ this.className = ''dropzone''; return false; } }(window));

ahora el CSS, para un poco de estilo ...

body{ font-family:Arial, Helvetica, sans-serif; font-size:12px; } .dropzone{ width:300px; height:300px; border:2px dashed #ccc; color:#ccc; line-height:300px; text-align:center; } .dropzone.dragover{ border-color:#000; color:#000; }


Estaba tratando de implementarlo yo mismo para un cuadro de carga de archivos donde el color de la caja cambiaría cuando los usuarios arrastraran un archivo al espacio.

Encontré una solución que es una buena combinación de Javascript y CSS. Supongamos que tiene una zona #drop con div con id #drop . Agregue esto a su Javascript:

$(''#drop'').on(''dragenter'', function() { $(this).addClass(''dragover''); $(this).children().addClass(''inactive''); }); $(''#drop'').on(''dragleave'', function() { $(this).removeClass(''dragover''); $(this).children().removeClass(''inactive''); });

A continuación, agregue esto a su CSS, para desactivar a todos los niños se clasificará .inactive :

#drop *.inactive { pointer-events: none; }

Por lo tanto, los elementos secundarios estarán inactivos mientras el usuario arrastre el elemento sobre el cuadro.


Esto es un poco feo, pero funciona maldita sea! ...

En su controlador ''dragenter'', guarde el evento.target (en una variable dentro de su cierre, o lo que sea), luego en su controlador ''dragleave'' solo active su código if event.target === el que guardó.

Si su ''dragenter'' está disparando cuando no lo desea (es decir, cuando está ingresando después de salir de los elementos secundarios), la última vez que se dispara antes de que el mouse abandone al padre, está en el padre, por lo que el padre siempre estará el ''dragenter'' final antes del ''dragleave'' previsto.

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


Esto parece ser un error de Chrome.

La única solución que podía pensar era crear un elemento de superposición transparente para capturar tus eventos: 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> ​


Finalmente encontré una solución con la que estoy contento. De hecho, encontré varias maneras de hacer lo que quiero, pero ninguna fue tan exitosa como la solución actual ... en una solución, experimenté un parpadeo frecuente como resultado de agregar / eliminar un borde al elemento #dropzone ... en otro, el borde nunca se eliminó si alejas el navegador.

De todos modos, mi mejor solución hacky es esta:

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

Esto funciona bastante bien, pero surgieron problemas en Firefox porque Firefox estaba invocando dos dragenter por lo que mi contador estaba apagado. Sin embargo, no es una solución muy elegante.

Luego me encontré con esta pregunta: Cómo detectar el evento dragleave en Firefox cuando se arrastra fuera de la ventana

Entonces tomé la respuesta y la apliqué a mi situación:

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

Esto es limpio y elegante, y parece estar funcionando en todos los navegadores que he probado hasta ahora (aún no he probado IE).


Mi version:

$(".dropzone").bind("dragover", function(e){ console.log(''dragover''); }); $(".dropzone").bind("dragleave", function(e) { var stopDrag = false; if (!e.relatedTarget) stopDrag = true; else { var parentDrop = $(e.relatedTarget).parents(''.dropzone''); if (e.relatedTarget != this && !parentDrop.length) stopDrag = true; } if (stopDrag) { console.log(''dragleave''); } });

Con este diseño:

<div class="dropzone"> <div class="inner-zone">Inner-zone</div> </div>

He hecho un volcado de clases de elementos para e.target , e.currentTarget , e.relatedTarget para los eventos dragover y dragleave .

Me mostró que al salir del bloque padre ( .dropzone ) e.relatedTarget no es hijo de este bloque, así que sé que estoy fuera de la zona de caída.


Mis dos centavos: esconde una capa sobre tu zona de caída, luego muéstrala cuando uses dragenter, y apunta la dragleave sobre ella.

Demostración: https://jsfiddle.net/t6q4shat/

HTML

<div class="drop-zone"> <h2 class="drop-here">Drop here</h2> <h2 class="drop-now">Drop now!</h2> <p>Or <a href="#">browse a file</a></p> <div class="drop-layer"></div> </div>

CSS

.drop-zone{ padding:50px; border:2px dashed #999; text-align:center; position:relative; } .drop-layer{ display:none; position:absolute; top:0; left:0; bottom:0; right:0; z-index:5; } .drop-now{ display:none; }

JS

$(''.drop-zone'').on(''dragenter'', function(e){ $(''.drop-here'').css(''display'',''none''); $(''.drop-now'').css(''display'',''block''); $(this).find(''.drop-layer'').css(''display'',''block''); return false; }); $(''.drop-layer'').on(''dragleave'', function(e){ $(''.drop-here'').css(''display'',''block''); $(''.drop-now'').css(''display'',''none''); $(this).css(''display'',''none''); return false; });


No me sentí satisfecho con ninguna de las soluciones presentadas aquí, porque no quiero perder el control sobre los elementos secundarios.

Así que he usado un enfoque lógico diferente, traduciéndolo a un plugin jQuery, llamado jquery-draghandler . No manipula absolutamente el DOM, garantizando altos rendimientos. Su uso es simple:

$(document).ready(function() { $(selector).draghandler({ onDragEnter: function() { // $(this).doSomething(); }, onDragLeave: function() { // $(this).doSomethingElse(); } }); });

Se trata sin problemas con el problema sin comprometer ninguna funcionalidad DOM.

Descarga, detalles y explicaciones en su jquery-draghandler .


Si está utilizando jQuery, consulte esto: https://github.com/dancork/jquery.event.dragout

Es realmente increíble.

Evento especial creado para manejar la verdadera funcionalidad de dragleave.

El evento HTML5 dragleave funciona más como mouseout. Este complemento fue creado para replicar la funcionalidad de estilo mouseleave mientras arrastra.

Ejemplo de uso:

$ (''# myelement''). on (''dragout'', function (event) {// YOUR CODE});

EDITAR: en realidad, no creo que dependa de jQuery, probablemente puedas usar el código incluso sin él.


Si no necesita vincular eventos a los elementos secundarios, siempre puede usar la propiedad de eventos de puntero.

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


Solución rápida súper simple para esto, no probada extensivamente pero que funciona en Chrome ahora mismo.

Disculpe el Coffeescript.

dragEndTimer = no document.addEventListener ''dragover'', (event) -> clearTimeout dragEndTimer $(''body'').addClass ''dragging'' event.preventDefault() return no document.addEventListener ''dragenter'', (event) -> $(''section'').scrollTop(0) clearTimeout dragEndTimer $(''body'').addClass ''dragging'' document.addEventListener ''dragleave'', (event) -> dragEndTimer = setTimeout -> $(''body'').removeClass ''dragging'' , 50

Esto soluciona el error de parpadeo de Chrome, o al menos la permutación que causaba problemas.


Tuve un problema similar y lo solucioné de esta manera:

El problema: la función de caída (ev) se activa cuando el usuario suelta un elemento en la "zona de colocación" (elemento ul), pero, desafortunadamente, también cuando el elemento cae en uno de sus elementos secundarios (li).

La solución:

function drop(ev) { ev.preventDefault(); data=ev.dataTransfer.getData(''Text''); if(ev.target=="[object HTMLLIElement]") {ev.target.parentNode.appendChild(document.getElementById(data));} else{ev.target.appendChild(document.getElementById(data));} }


Use el greedy : true para el niño como una función de droppable . Luego, inicie solo el evento al que se hizo clic en la primera capa.