'dragleave' de elementos primarios se dispara al arrastrar elementos secundarios

163

Visión general

Tengo la siguiente estructura HTML y he adjuntado los eventos dragentery dragleaveal <div id="dropzone">elemento.

<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 dragenterevento se dispara como se esperaba. Sin embargo, cuando muevo el mouse sobre un elemento secundario, como <div id="drag-n-drop">, el dragenterevento se activa para el <div id="drag-n-drop">elemento y luego el dragleaveevento se activa para el <div id="dropzone">elemento.

Si vuelvo a pasar el cursor sobre el <div id="dropzone">elemento nuevamente, el dragenterevento se activa nuevamente, lo cual es genial, pero luego el dragleaveevento se activa para el elemento secundario que acaba de salir, por lo removeClassque se ejecuta la instrucción, que no es genial.

Este comportamiento es problemático por 2 razones:

  1. Solo estoy adjuntando dragentery dragleaveal, <div id="dropzone">así que no entiendo por qué los elementos secundarios también tienen estos eventos adjuntos.

  2. ¡Todavía estoy arrastrando sobre el <div id="dropzone">elemento mientras se cierne sobre sus hijos, así que no quiero dragleavedisparar!

jsFiddle

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

Pregunta

Entonces ... ¿cómo puedo hacerlo de tal manera que cuando estoy arrastrando un archivo sobre el <div id="dropzone">elemento, dragleaveno se dispara incluso si estoy arrastrando sobre cualquier elemento secundario ... solo debería dispararse cuando salgo del <div id="dropzone">elemento ... Al desplazarse / arrastrarse por cualquier lugar dentro de los límites del elemento no se debe activar el dragleaveevento.

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

Parece que Google y Dropbox han resuelto esto, pero su código fuente es reducido / complejo, por lo que no he podido resolverlo a partir de su implementación.

Hristo
fuente
Evite la propagación de eventos a través dee.stopPropagation();
Blender
Creo que ya estoy ... echa un vistazo a la actualización
Hristo
¿Puedes publicar una demostración en algún lugar de jsfiddle.net que la gente pueda jugar con ella?
Blender
@Blender ... ¡claro, dame un par de minutos!
Hristo
@Blender ... Actualicé mi pregunta con un violín
Hristo

Respuestas:

171

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

.child-elements {
  pointer-events: none;
}
Ben Rudolph
fuente
44
¡Mejor solución! Ni siquiera pensé que podría resolver el problema usando CSS de una manera tan elegante. ¡Muchas gracias!
serg66
1
Oh si. ¡Eso es todo!
mcmlxxxiii
23
El único inconveniente de este enfoque es que nuclea todos los eventos de puntero en elementos secundarios (por ejemplo, ya no pueden tener :hoverestilos separados o controladores de eventos de clic). En caso de que quiera preservar esos eventos, aquí hay otra solución que he estado usando: bensmithett.github.io/dragster
Ben
2
Combine con el selector de niños CSS para llegar a todos los niños de su zona de caída: .dropzone * {pointer-events: none;}
Philipp F
77
Ya está utilizando jQuery, así que solo $('.element').addClass('dragging-over')al elemento que está arrastrando y $('.element').removeClass('dragging-over')cuando lo arrastra. Luego en tu CSS puedes tener .element.dragging-over * { pointer-events: none; }. Eso elimina los eventos de puntero de todos los elementos secundarios solo cuando se arrastra.
Gavin
56

Finalmente encontré una solución con la que estoy contento. En realidad, encontré varias formas de hacer lo que quiero, pero ninguna fue tan exitosa como la solución actual ... en una solución, experimenté parpadeos frecuentes como resultado de agregar / eliminar un borde al #dropzoneelemento ... en otra, el borde nunca se eliminó si te alejas del 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 dragenterhaciendo doble invocación, así que mi contador estaba apagado. Pero, sin embargo, no es una solución muy elegante.

Luego me topé con esta pregunta: cómo detectar el evento dragleave en Firefox al arrastrar 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).

Hristo
fuente
Esta es la mejor solución hasta ahora. El único problema es que este código no funciona cuando agrega un archivo, lo elimina y arrastra otro archivo sobre él. Solo después de aumentar HoverEnd y aumentar HoverStart nuevamente, este código funciona nuevamente.
MysticEarth
@ MysticEarth, ¿puedes armar un jsFiddle para demo cuando no funciona?
Hristo
1
Gracias por su respuesta. Esto fue realmente útil, ya que la interfaz de arrastrar y soltar HTML5 y los oyentes de eventos pueden ser realmente dolorosos. Esta es la mejor manera de manejar los eventos locos, especialmente cuando desea manejar múltiples instancias de elementos de zona de caída.
Nico O
¿Por qué necesitamos llamar a stopPropagation () preventDefault () y devolver false. Devolver falso debería ser suficiente. Deberia ?
Dejo
Es redundante, pero se llama stopPropagation()y preventDefault()se prefiere. Yo soltaría el return false.
Peeja
38

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

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

Si su 'dragenter' está disparando cuando no lo desea (es decir, cuando ingresa después de dejar elementos secundarios), entonces 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 'grageador' 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");
        }
    });

}());
hacklikecrack
fuente
Lo intentaré más tarde. En todo caso, se ve más limpio que mi solución, pero no puedo estar seguro de cuán compatible es entre navegadores solo con mirarlo. ¿Dónde has probado esto?
Hristo
1
solo Chrome, y puede funcionar solo bajo el supuesto de que existe una brecha física entre los elementos secundarios y el contenedor.
hacklikecrack
55
Mi respuesta favorita aquí. No se siente feo en absoluto :)
Matt Way
1
Funciona para mí en IE, Chrome, FF
Vicentiu Bacioiu
1
Esto no funciona si el arrastre comienza en los niños. Por ejemplo, arrastrar desde algo desde otra ventana de manera que el arrastre comience dentro del niño.
FINDarkside
29

Al principio, acepté que la gente descartara el pointer-events: noneenfoque. Pero luego me pregunté:

¿Realmente necesita eventos de puntero para trabajar en los elementos secundarios mientras se está arrastrando ?

En mi caso, tengo muchas cosas que suceden en los niños, por ejemplo, desplazar el mouse para mostrar 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, uso algo como esto para desactivar selectivamente los eventos de puntero para todos los nodos secundarios del contenedor primario:

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

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

bargar
fuente
1
Esta solución es a la vez la más simple y es (hasta donde puedo decir) a prueba de completo. Simplemente agregue la clase en el dragstartevento y elimínela dragend.
cmann
Esta es una solución bastante buena pero no perfecta. Si el arrastre comienza en el elemento secundario (arrastrando desde otra ventana), dragEnter nunca se activa. Probablemente también sea posible mover el mouse lo suficientemente rápido como para que dragEnter no se active cuando se mueva hacia el niño, pero no esté 100% seguro de eso.
FINDarkside
12

Esto parece ser un error de Chrome.

La única solución que se me ocurrió fue crear un elemento de superposición transparente para capturar sus 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>
Licuadora
fuente
Gracias por la respuesta, Blender. Sin embargo, ya lo he intentado. El problema con esta solución es que el elemento de superposición "superpone" a los elementos secundarios, con los que necesito interactuar ... por lo que tener una superposición deshabilita la interacción que necesito con los elementos secundarios ... piense en ello como un archivo arrastrar-n -caja de caída ... puede arrastrar y soltar o puede hacer clic en el elemento de entrada para seleccionar sus archivos.
Hristo
He
Hristo
Estoy confundido. ¿Por qué necesita interactuar también con los elementos secundarios?
Blender
En mi caso particular, un elemento secundario de la "zona de caída" es un botón de selección de archivo ... así que si no quiero arrastrar y soltar archivos, puedo seleccionarlos usando el elemento de entrada. Pero con la superposición ... No puedo hacer clic en el elemento de entrada. ¿tener sentido?
Hristo
@Blender La mayoría de los casos necesitamos arrastrar un archivo desde el escritorio, no un elemento desde la página, por lo que esto no funciona.
Mārtiņš Briedis
5

El problema es que sus elementos dentro de las zonas de caída son, por supuesto, parte de la zona de caída y cuando ingresa a los elementos secundarios, deja a los padres. Resolver esto no es fácil. Puede intentar agregar eventos a los niños también agregando su clase nuevamente a los padres.

$("#dropzone,#dropzone *").on('dragenter', function(event) {

    // add a class to #dropzone

    event.stopPropagation(); // might not be necessary
    event.preventDefault();
    return false;
});

Sus eventos aún se dispararán varias veces, pero nadie lo verá.

// Editar: 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 la zona de caída.

Oliver
fuente
2
Sin embargo, eso no resolverá el problema. El verdadero problema es dragleave... cuando dejo a un niño, aún podría estar dentro de los padres, #dropzonepero eso no volverá a desencadenar el dragenterevento :(
Hristo
¿Estás seguro de que el grabador no se vuelve a disparar?
Oliver
Ah, claro ... se dispara de nuevo, sin embargo, después de que se dispara, se dispara el dragleaveevento para el elemento hijo que acaba de salir, por lo que nuevamente removeClassse ejecuta la instrucción
Hristo
No puedo definir dragleavesolo para el #dropzone... si pudiera, no tendría este problema.
Hristo
No, quise decir no agregar eventos para dragleave a los niños.
Oliver
4

Como se menciona en esta respuesta , puede evitar que los nodos secundarios se activen en eventos, pero si necesita vincular algunos eventos, haga lo siguiente:

#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");
});
Vahid Ashrafian
fuente
2
Para mí, esto hace que la superposición parpadee constantemente.
Andrey Mikhaylov - lolmaus
3

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

Es realmente asombroso.

Evento especial creado para manejar la verdadera funcionalidad de dragleave.

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

Ejemplo de uso:

$ ('# myelement'). on ('dragout', function (event) {// SU CÓDIGO});

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

nkkollaw
fuente
¡Guauu! Arrastrar es lo ÚNICO que funcionó para mi diseño con un montón de elementos dragleavesecundarios activados a pesar de que no he salido de la zona de caída principal.
Mark Kasson
Después de luchar con este problema durante la última hora, dragoutfue la única solución que funcionó para mí.
Jason Hazel
2

@hristo Tengo una solución mucho más elegante. Comprueba que si eso es algo que podrías usar.

Su esfuerzo no fue malgastado después de todo. Al principio pude usar el tuyo, pero tuve diferentes problemas en FF, Chrome. Después de pasar tantas horas, recibí esa sugerencia funcionando exactamente como estaba previsto.

Así es como es la implementación. También aproveché las señales visuales para guiar 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
});
visitasb
fuente
parece que te faltan las definiciones para algunas variables fuera de alcance, supongovar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen
@TimoHuovinen sí, correcto. Ambos dropZoneHideDelay, dropZoneVisibleson necesarios en dos 'on'eventos de documentos en mi implementación.
visitasb
2

Mis dos centavos: esconde una capa sobre tu zona de caída y luego muéstrala cuando grabas en el centro, y apunta al 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;
});
jeremie.b
fuente
Simple y efectivo! Estaba siguiendo la ruta stopPropagation () y no me gustaba aplicar el controlador de eventos a todos los niños. Parecía desordenado, pero esto funciona bien y es fácil de implementar. Buena idea.
Jon Catmull
1

Entonces, para mí, el enfoque pointer-events: none;no funcionó demasiado 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 dragleaveel elemento primario (en un elemento secundario) o dragoverun elemento secundario. espero que esto ayude :)

* La clase '.active' agrego cuando dragenter o dragleave. Pero si está trabajando sin eso, simplemente deje la clase ausente.

MMachinegun
fuente
1
Intenté esto en un componente EmberJS D&D y agregar la clase 'activa' programáticamente es demasiado lento aproximadamente el 40% del tiempo que el mouse ingresa al div padre (vista de ascua). No estoy seguro de por qué. Pero dejando eso y funciona muy bien, así que gracias por esta solución simple. Probado en una Mac en las últimas versiones (a partir de hoy) de Safari, Chrome, Firefox.
rmcsharry
1

Solución rápida súper simple para esto, no se ha probado exhaustivamente pero funciona en Chrome en este momento.

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 me causaba problemas.

Dom Vinyard
fuente
1

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 vaciado de las clases de elementos para e.target, e.currentTarget, e.relatedTargetpara ambos dragovery dragleaveeventos.

Me demostró que al dejar el bloque padre ( .dropzone) e.relatedTargetno es un hijo de este bloque, así que sé que estoy fuera de la zona de caída.

mortalis
fuente
0

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

Eche un vistazo a este violín <- intente arrastrar algún archivo 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 lo que está sucediendo aquí.
Solo echa un vistazo al violín, ya sabes.

Diego T. Yamaguchi
fuente
0

He tenido un problema similar y lo solucioné de esta manera:

El problema: la función drop (ev) se activa cuando el usuario coloca un elemento en la "zona drop" (elemento ul), pero, desafortunadamente, también cuando el elemento se cae en uno de sus elementos secundarios (elementos 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));} 
} 
Pao
fuente
0

Intenté implementar esto yo mismo para un cuadro de carga de archivos donde el color del cuadro cambiaría cuando los usuarios arrastraran un archivo al espacio.

Encontré una solución que es una buena combinación de Javascript y CSS. Digamos que tiene una zona desplegable divcon 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');
});

Luego, agregue esto a su CSS, para inactivar todos los niños clasificarán .inactive:

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

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

David A
fuente
0

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 en un complemento jQuery, llamado jquery-draghandler . No manipula absolutamente el DOM, garantizando altas prestaciones. Su uso es simple:

$(document).ready(function() {

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

});

Aborda perfectamente el problema sin comprometer ninguna funcionalidad DOM.

Descarga, detalles y explicaciones en su repositorio Git .

Luca Fagioli
fuente
¿Cómo obtengo el evento drag enter en su complemento?
Woody
Realmente no he probado su directiva, pero creo que no funciona si el elemento padre (A) en realidad no rodea el elemento que quiero verificar (B). Por ejemplo, si no hay relleno entre el borde inferior de B y el borde inferior de A, entonces dragenter nunca se disparará para el elemento A, ¿verdad?
marcelj
@marcelj El elemento A puede ser el documento del cuerpo mismo, por lo que realmente no necesita un elemento circundante (el cuerpo hace el trabajo) con un relleno mínimo. La limitación que encontré más adelante con este enfoque es que si trabajas con framesy tu elemento está en el límite. En ese caso, no sugiero este enfoque.
Luca Fagioli
0

De hecho, me gusta lo que veo en https://github.com/lolmaus/jquery.dragbetter/pero quería compartir una posible alternativa. Mi estrategia general era aplicar un estilo de fondo a la zona de caída (no a sus elementos secundarios) cuando la graficaba a ella o a cualquier elemento secundario (mediante burbujeo). Luego elimino el estilo al dragleaving la zona de caída. La idea era cuando me mudaba a un niño, incluso si elimino el estilo de la zona de caída cuando lo dejo (los fuegos de dragleave), simplemente volvería a aplicar el estilo a la zona de caída principal al ingresar a cualquier niño. El problema es, por supuesto, que cuando me muevo de la zona de caída a un niño de la zona de caída, la gragera se dispara sobre el niño antes de la caída del dragle, por lo que mis estilos se aplicaron fuera de orden. La solución para mí fue usar un temporizador para forzar el evento dragenter de nuevo a la cola de mensajes, lo que me permitió procesarlo DESPUÉS de la secuencia de descarga. 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 de la cantidad de niños en la zona de caída. Estoy un poco incómodo con el tiempo de espera que garantiza la secuenciación de eventos, pero parece funcionar bastante bien para mi caso de uso.

mefópolis
fuente
0

Estaba tratando de implementar esto yo mismo, y tampoco quería Jquery ni ningún complemento.

Quería manejar la carga del archivo, de la siguiente manera me pareció mejor:

Estructura de archivos:

--- / uploads {directorio de cargas}

--- /js/slyupload.js {archivo javascript}

--- index.php

--- upload.php

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

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

CSS:

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

Ande Caleb
fuente
0

Lo siento, es javascript, no jquery, pero para mí es la forma más lógica de resolverlo. El navegador debe llamar a dropleave (del elemento anterior) antes de dropenter (ofr the new element, ya que algo no puede ingresar a otra cosa antes de dejar lo primero, ¡no entiendo por qué lo hicieron! Así que solo necesita retrasar de dropleave así:

function mydropleave(e)
{
    e.preventDefault();
    e.stopPropagation();

    setTimeout(function(e){ //the things you want to do },1);
}

Y el dropenter ocurrirá después del dropleave, ¡eso es todo!

Entretoizar
fuente
-1

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

Adán
fuente