'trascinamento' dell'elemento genitore viene generato quando si trascina su elementi figlio


163

Panoramica

Ho la seguente struttura HTML e ho allegato gli eventi dragentere all'elemento.dragleave<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

Quando trascino un file sopra <div id="dropzone">, l' dragenterevento viene generato come previsto. Tuttavia, quando sposto il mouse su un elemento figlio, ad esempio <div id="drag-n-drop">, l' dragenterevento viene generato per l' <div id="drag-n-drop">elemento e quindi l' dragleaveevento viene generato per l' <div id="dropzone">elemento.

Se passo di nuovo sopra l' <div id="dropzone">elemento, l' dragenterevento viene nuovamente generato, il che è interessante, ma quindi l' dragleaveevento viene generato per l'elemento figlio appena lasciato, quindi l' removeClassistruzione viene eseguita, che non è interessante.

Questo comportamento è problematico per 2 motivi:

  1. Sto solo attaccando dragentere dragleaveal, <div id="dropzone">quindi non capisco perché anche agli elementi per bambini siano associati questi eventi.

  2. Sto ancora trascinando l' <div id="dropzone">elemento mentre ho il mouse sopra i suoi figli, quindi non voglio dragleavesparare!

jsFiddle

Ecco un jsFiddle da armeggiare con: http://jsfiddle.net/yYF3S/2/

Domanda

Quindi ... come posso renderlo tale che quando trascino un file sopra l' <div id="dropzone">elemento, dragleavenon si attiva anche se sto trascinando su tutti gli elementi figlio ... dovrebbe sparare solo quando lascio l' <div id="dropzone">elemento .. il passaggio del mouse / trascinamento in qualsiasi punto all'interno dei limiti dell'elemento non deve attivare l' dragleaveevento.

Ho bisogno che questo sia compatibile con più browser, almeno nei browser che supportano il trascinamento della selezione HTML5, quindi questa risposta non è adeguata.

Sembra che Google e Dropbox lo abbiano capito, ma il loro codice sorgente è minimizzato / complesso, quindi non sono stato in grado di capirlo dalla loro implementazione.


Previeni la propagazione degli eventi tramitee.stopPropagation();
Blender il

Penso di essere già ... controlla l'aggiornamento
Hristo

Puoi pubblicare una demo da qualche parte su jsfiddle.net che la gente può armeggiare con essa?
Blender,

@Blender ... certo, dammi un paio di minuti!
Hristo,

@Blender ... Ho aggiornato la mia domanda con un violino
Hristo,

Risposte:


171

Se non è necessario associare eventi agli elementi figlio, è sempre possibile utilizzare la proprietà pointer-events.

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

4
Soluzione migliore! Non pensavo nemmeno di poter risolvere il problema usando i CSS in un modo così elegante. Grazie mille!
serg66,

1
O si. Questo è!
mcmlxxxiii

23
L'unico aspetto negativo di questo approccio è che annulla tutti gli eventi puntatore su elementi figlio (ad esempio, non possono più avere :hoverstili separati o gestori di eventi click). Nel caso in cui desideri preservare quegli eventi, ecco un'altra soluzione alternativa che sto usando: bensmithett.github.io/dragster
Ben

2
Combina con il selettore figlio css per raggiungere tutti i bambini del tuo dropzone: .dropzone * {pointer-events: none;}
Philipp F

7
Stai già utilizzando jQuery, quindi solo $('.element').addClass('dragging-over')per l'elemento su cui stai trascinando e $('.element').removeClass('dragging-over')quando trascini. Quindi nel tuo CSS puoi avere .element.dragging-over * { pointer-events: none; }. Ciò rimuove gli eventi del puntatore da tutti gli elementi figlio solo durante il trascinamento.
Gavin,

56

Finalmente ho trovato una soluzione di cui sono felice. In realtà ho trovato diversi modi per fare quello che voglio, ma nessuno ha avuto il successo della soluzione attuale ... in una soluzione, ho avuto frequenti sfarfallamenti a seguito dell'aggiunta / rimozione di un bordo #dropzoneall'elemento ... in un altro, il bordo non è mai stato rimosso se ti allontani dal browser.

Comunque, la mia migliore soluzione hacky è questa:

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

Funziona abbastanza bene, ma i problemi si sono verificati in Firefox perché Firefox aveva doppio invocazione, dragenterquindi il mio contatore era spento. Tuttavia, non è una soluzione molto elegante.

Quindi mi sono imbattuto in questa domanda: come rilevare l'evento dragleave in Firefox quando si trascina fuori dalla finestra

Quindi ho preso la risposta e l'ho applicata alla mia situazione:

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

Questo è pulito ed elegante e sembra funzionare in tutti i browser che ho testato finora (non ho ancora testato IE).


Questa è la soluzione migliore finora. L'unico problema è che questo codice non funziona quando aggiungi un file, rimuovilo e trascina un altro file su di esso. Solo dopo aver sollevato HoverEnd e aver rilanciato HoverStart questo codice funziona di nuovo.
MysticEarth

@MysticEarth puoi mettere insieme un jsFiddle per la demo quando non funziona?
Hristo,

1
La ringrazio per la risposta. Questo è stato davvero utile, poiché l'interfaccia di trascinamento della selezione HTML5 e i listener di eventi possono essere davvero dolorosi. Questo è il modo migliore per gestire gli eventi folli, soprattutto quando si desidera gestire più istanze di elementi della zona di rilascio.
Nico O

Perché dobbiamo chiamare stopPropagation () preventDefault () e restituire false. Return false dovrebbe essere sufficiente. Dovrebbe ?
Dejo,

È ridondante, ma chiama stopPropagation()ed preventDefault()è preferito. Lascerei cadere il return false.
Peeja,

38

Questo è un po 'brutto ma funziona dannazione! ...

Sul gestore "dragenter" memorizza event.target (in una variabile all'interno della chiusura, o altro), quindi nel gestore "dragleave" attiva il codice solo se event.target === quello che hai archiviato.

Se il tuo 'dragenter' sta sparando quando non lo vuoi (cioè quando entra dopo aver lasciato elementi figlio), l'ultima volta che spara prima che il mouse lasci il genitore, è sul genitore, quindi il genitore sarà sempre il 'dragenter' finale prima 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");
        }
    });

}());

Ci proverò più tardi. Semmai, sembra più pulito della mia soluzione, ma non posso essere sicuro di quanto sia compatibile con più browser solo guardandolo. Dove l'hai provato?
Hristo,

1
solo Chrome, e potrebbe funzionare solo supponendo che vi sia un divario fisico tra gli elementi figlio e il contenitore.
hacklikecrack,

5
La mia risposta preferita qui. Non si sente affatto brutto :)
Matt Way

1
Funziona per me in IE, Chrome, FF
Vicentiu Bacioiu,

1
Questo non funziona se il trascinamento inizia sui bambini. Ad esempio, il trascinamento da qualcosa da un'altra finestra in un modo che trascina inizia all'interno del bambino.
FINDarkside,

29

Inizialmente, sono stato d'accordo con la gente che ha scartato l' pointer-events: noneapproccio. Ma poi mi sono chiesto:

Hai davvero bisogno di eventi puntatore per lavorare sugli elementi figlio mentre è in corso il trascinamento ?

Nel mio caso, ho molte cose in corso nei bambini, ad esempio hover per mostrare pulsanti per azioni aggiuntive, modifica in linea, ecc ... Tuttavia, nulla di tutto ciò è necessario o addirittura desiderato durante un trascinamento.

Nel mio caso, utilizzo qualcosa del genere per disattivare selettivamente gli eventi puntatore per tutti i nodi figlio del contenitore padre:

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

Usa il tuo approccio preferito per aggiungere / rimuovere la classe dragging-in-progressnei gestori dragEnter/ dragLeaveevent, come ho fatto o fatto lo stesso in dragStart, et. al.


1
Questa soluzione è la più semplice ed è (per quanto posso dire) a prova di tutto. Basta aggiungere la classe dragstartnell'evento e rimuoverla dragend.
cmann

Questa è una soluzione piuttosto carina ma non perfetta. Se il trascinamento inizia sull'elemento figlio (trascinando da un'altra finestra), dragEnter non viene mai attivato. Probabilmente è anche possibile spostare il mouse abbastanza velocemente da non trascinare dragEnter quando si passa al figlio, ma non è sicuro al 100%.
FINDarkside,

12

Questo sembra essere un bug di Chrome.

L'unica soluzione che mi è venuta in mente è stata quella di creare un elemento overlay trasparente per catturare i tuoi eventi: 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>

grazie per la risposta, Blender. L'ho già provato, comunque. Il problema con questa soluzione è che l'elemento overlay "sovrappone" gli elementi children, con i quali devo interagire ... quindi avere un overlay disabilita l'interazione di cui ho bisogno con i bambini ... pensalo come un file drag-n casella di selezione ... è possibile trascinare e rilasciare oppure fare clic sull'elemento di input per selezionare i file.
Hristo,

ecco un violino di esempio che la tua soluzione non soddisfa ... jsfiddle.net/UnsungHero97/yYF3S/11
Hristo

Non ho capito bene. Perché devi interagire anche con gli elementi figlio?
Blender

Nel mio caso particolare, un elemento figlio del "dropzone" è un pulsante di selezione file ... quindi se non voglio trascinare e rilasciare i file, posso selezionarli usando l'elemento di input. Ma con la sovrapposizione ... Non posso fare clic sull'elemento di input. ha senso?
Hristo,

@Blender La maggior parte dei casi è necessario trascinare un file dal desktop, non un elemento dalla pagina, quindi questo non funziona.
Mārtiņš Briedis,

5

Il problema è che i tuoi elementi all'interno delle zone di rilascio fanno ovviamente parte della zona di rilascio e quando entri nei bambini, lasci il genitore. Risolvere questo non è facile. Potresti provare ad aggiungere eventi anche ai bambini aggiungendo di nuovo la tua classe al genitore.

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

    // add a class to #dropzone

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

I tuoi eventi verranno comunque attivati ​​più volte ma nessuno lo vedrà.

// Modifica: usa l'evento dragmove per sovrascrivere permanentemente l'evento dragleave:

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

    // add a class to #dropzone

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

Definire l'evento dragleave solo per la zona di rilascio.


2
ciò non risolverà il problema però. Il vero problema è dragleave... quando lascio un bambino, potrei essere ancora all'interno del genitore, #dropzonema questo non accenderà di dragenternuovo l' evento :(
Hristo

Sei sicuro che il dragenter non venga più sparato?
Oliver,

Ah giusto ... viene sparato di nuovo, tuttavia, dopo che è stato generato, l' dragleaveevento viene generato per l'elemento figlio appena lasciato, quindi di nuovo l' removeClassistruzione viene eseguita
Hristo,

Non posso definire dragleavesolo per il #dropzone... se potessi, non avrei questo problema.
Hristo,

No, intendevo non aggiungere eventi per dragleave ai bambini.
Oliver,

4

Come menzionato in questa risposta , puoi impedire ai nodi figlio di attivarsi sugli eventi, ma se devi associare alcuni eventi, procedi nel seguente modo :

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

E aggiungi questo al tuo codice JS:

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

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

2
Per me questo si traduce in un overlay che lampeggia costantemente.
Andrey Mikhaylov - lolmaus,

3

Se stai usando jQuery, dai un'occhiata a: https://github.com/dancork/jquery.event.dragout

È davvero fantastico.

Evento speciale creato per gestire la vera funzionalità dragleave.

L'evento dragleave HTML5 funziona più come il mouseout. Questo plugin è stato creato per replicare la funzionalità dello stile di mouseleave durante il trascinamento.

Esempio di utilizzo:

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

EDIT: in realtà, non penso che dipenda da jQuery, probabilmente puoi semplicemente usare il codice anche senza di esso.


Wow! Il dragout è l'unica cosa che ha funzionato per il mio layout con molti elementi figlio attivati dragleaveanche se non ho lasciato il dropzone principale.
Mark Kasson,

Dopo aver combattuto con questo problema per l'ultima ora, è dragoutstata l'unica soluzione che ha funzionato per me.
Jason Hazel,

2

@hristo ho una soluzione molto più elegante. Controlla che se è qualcosa che potresti usare.

Dopo tutto, i tuoi sforzi non sono stati sprecati. Sono riuscito a utilizzare il tuo all'inizio, ma ho avuto diversi problemi in FF, Chrome. Dopo aver trascorso così tante ore ho avuto il suggerimento di funzionare esattamente come previsto.

Ecco come è l'implementazione. Ho anche approfittato di segnali visivi per guidare correttamente gli utenti sulla zona di rilascio.

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

sembra che manchi le definizioni per alcune variabili fuori campo, presumovar dropZoneHideDelay=70, dropZoneVisible=true;
Timo Huovinen,

@TimoHuovinen sì, giusto. Entrambi dropZoneHideDelay, dropZoneVisiblesono necessari in due 'on'eventi del documento nella mia implementazione.
visita il

2

I miei due centesimi: nascondi un livello sopra la tua zona di rilascio, quindi mostralo quando trascini il mouse e scegli come bersaglio la foglia di trascinamento su di esso.

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

Semplice ed efficace! Stavo seguendo il percorso stopPropagation () e non mi piaceva applicare il gestore eventi a tutti i bambini. Sembrava disordinato, ma funziona bene ed è semplice da implementare. Buona idea.
Jon Catmull,

1

Quindi per me l'approccio pointer-events: none;non ha funzionato troppo bene ... Quindi ecco la mia soluzione alternativa:

    #dropzone {
        position: relative;
    }

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

In questo modo non è possibile per dragleaveil genitore (su un figlio) odragover un elemento figlio. spero che questo ti aiuti :)

* La classe '.active' che aggiungo quando io dragentero dragleave. Ma se lavori senza questo, lascia solo la lezione.


1
L'ho provato in un componente D&D di EmberJS e l'aggiunta programmatica della classe "attiva" è troppo lenta circa il 40% delle volte in cui il mouse entra nel div genitore (ember-view). Non so perché. Ma tralasciando tutto e funziona alla grande, quindi grazie per questa semplice soluzione. Testato su Mac con le ultime versioni (ad oggi) di Safari, Chrome, Firefox.
rmcsharry,

1

Soluzione rapida super semplice per questo, non ampiamente testata ma funziona in Chrome in questo momento.

Scusa il caffè.

  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

Questo risolve il bug dello sfarfallio di Chrome, o almeno la sua permutazione che mi stava causando problemi.



1

La mia versione:

$(".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 questo layout:

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

Ho fatto una discarica di classi di elementi per e.target, e.currentTarget, e.relatedTargetper entrambi dragovere dragleavegli eventi.

Mi ha mostrato che lasciare il blocco genitore ( .dropzone) e.relatedTargetnon è figlio di questo blocco, quindi so di essere fuori dal dropzone.


0

Ecco una delle soluzioni più semplici ● ︿ ●

Dai un'occhiata a questo violino <- prova a trascinare alcuni file nella casella

Puoi fare qualcosa del genere:

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

Con ciò dovresti già sapere cosa sta succedendo qui.
Dai un'occhiata al violino, lo sai.


0

Ho avuto un problema simile e l'ho risolto in questo modo:

Il problema: la funzione drop (ev) viene attivata quando l'utente rilascia un elemento nella "zona di rilascio" (elemento ul), ma, sfortunatamente, anche quando l'elemento viene rilasciato in uno dei suoi figli (elementi li).

La correzione:

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

0

Stavo cercando di implementarlo da solo per una casella di caricamento file in cui il colore della casella cambiava quando gli utenti trascinavano un file nello spazio.

Ho trovato una soluzione che è un bel mix di Javascript e CSS. Supponi di avere una zona trascinabile divcon id #drop. Aggiungi questo al tuo Javascript:

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

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

Quindi, aggiungi questo al tuo CSS, per inattivare tutti i bambini classificheranno .inactive:

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

Pertanto, gli elementi figlio rimarranno inattivi finché l'utente trascina l'elemento sopra la casella.


0

Non mi sono sentito soddisfatto di nessuna delle soluzioni alternative presentate qui, perché non voglio perdere il controllo sugli elementi dei bambini.

Quindi ho usato un approccio logico diverso, traducendolo in un plugin jQuery, chiamato jquery-draghandler . Non manipola assolutamente il DOM, garantendo alte prestazioni. Il suo utilizzo è semplice:

$(document).ready(function() {

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

});

Gestisce perfettamente il problema senza compromettere la funzionalità DOM.

Scarica, dettagli e spiegazioni sul suo repository Git .


Come ottengo l'evento drag enter nel tuo plugin?
Woody,

Non ho ancora testato la tua direttiva, ma penso che non funzioni se l'elemento genitore (A) non circonda effettivamente l'elemento che voglio controllare (B). Ad esempio, se non c'è imbottitura tra il bordo inferiore di B e il bordo inferiore di A, il dragenter non verrà mai attivato per l'elemento A, giusto?
marcelj,

@marcelj L'elemento A può essere il documento del corpo stesso, quindi non hai davvero bisogno di un elemento circostante (il corpo fa il lavoro) con un'imbottitura minima. La limitazione che ho trovato in seguito con questo approccio è che se lavori con se frameil tuo elemento è al limite. In tal caso, non suggerisco questo approccio.
Luca Fagioli,

0

In realtà mi piace quello che vedo https://github.com/lolmaus/jquery.dragbetter/ma volevo condividere una possibile alternativa. La mia strategia generale era quella di applicare uno stile di sfondo alla zona di lancio (non ai suoi figli) durante il trascinamento di esso o di qualsiasi suo figlio (tramite bolle). Quindi rimuovo lo stile quando trascino la zona di trascinamento. L'idea è stata quando mi sono trasferito a un bambino, anche se rimuovo lo stile dalla zona di trascinamento quando lo lascio (il dragleave si attiva), riapplicerei semplicemente lo stile alla zona di rilascio principale trascinando qualsiasi bambino. Il problema è ovviamente che quando si passa dalla zona di lancio a una figlia della zona di rilascio, il dragenter viene sparato sul bambino prima del dragleave, quindi i miei stili sono stati applicati fuori servizio. La soluzione per me era usare un timer per forzare l'evento dragenter di nuovo nella coda dei messaggi, permettendomi di elaborarlo DOPO il trascinamento. Ho usato una chiusura per accedere all'evento sul callback del timer.

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

Questo sembra funzionare in Chrome, ovvero Firefox e funziona indipendentemente dal numero di bambini nella zona di rilascio. Sono un po 'a disagio per il timeout che garantisce la ricomposizione degli eventi, ma sembra funzionare abbastanza bene per il mio caso d'uso.


0

Stavo cercando di implementarlo da solo, e non volevo neanche Jquery o nessun plugin.

Volevo gestire il caricamento del file, nel modo che preferisco:

Struttura dei file:

--- / uploads {directory uploads}

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

--- index.php

--- upload.php

--- styles.css {solo un po 'di stile ..}

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

Quindi questo è il file Javascript allegato :: '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;
}


0

Mi dispiace che javascript non sia jquery, ma per me è il modo più logico per risolverlo. Il browser dovrebbe chiamare dropleave (dell'elemento precedente) prima di dropenter (ofr il nuovo elemento, poiché qualcosa non può entrare in qualcos'altro prima di aver lasciato la prima cosa, non capisco perché l'hanno fatto! Quindi devi solo ritardare de dropleave così:

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

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

E il dropenter accadrà dopo il dropleave, tutto qui!


-1

Utilizzare il greedy : trueper il bambino in funzione di droppable. Quindi avvia solo l'evento selezionato sul primo livello.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.