2015-01-29 12 views
11

Durante la scrittura di app Web che hanno richiesto l'input di file, volevo utilizzare il trascinamento della selezione, ma non volevo solo una piccola dropzone sulla pagina. Ho pensato che sarebbe stato più comodo se potessi cadere da qualche parte sulla pagina. Fortunatamente, l'evento window.ondrop si attiva ovunque nella pagina, ma volevo qualche effetto di fantasia per mostrare visivamente all'utente che era possibile il trascinamento/rilascio.Intera pagina come dropzone per il trascinamento della selezione

Per fare questo, tutto ciò che era necessario era di rilevare quando un file è stato trascinato nella finestra, e quando fu trascinato fuori, per innescare un effetto che ha mostrato l'utente che l'applicazione è stata drag-enabled. Risulta che gli eventi di trascinamento non sono così convenienti. Supponevo che lo window.ondragenter si attivasse solo una volta, quando l'utente entrava nella pagina. Poi, quando hai lasciato la finestra, si innesca window.ondragleave. Sbagliato. Spara costantemente mentre il mouse si sposta su elementi figlio nella pagina.

Ho controllato quali proprietà erano disponibili nell'oggetto evento, cercando di trovare qualcosa che potesse isolare ciò di cui avevo bisogno, ma non funzionava. Il più lontano che ho ottenuto è stato in grado di cambiare il colore di sfondo di body. E solo se non c'era nient'altro sulla pagina.

Tonnellate di siti di caricamento file hanno capito bene. Imgur e WeTransfer per esempio. I loro siti erano tutti codificati in spahetti e compressi fino al punto di illeggibilità, e non riuscivo a trovare nulla sull'argomento su google.

Quindi, come può essere fatto?

risposta

17

Il trucco è quello di utilizzare una zona di lancio che copre l'intera pagina, e la cache di targetwindow.ondragenter da confrontare con il target di window.ondragleave.

In primo luogo, quello di lancio:

<style> 
div.dropzone 
{ 
    /* positions to point 0,0 - required for z-index */ 
    position: fixed; top: 0; left: 0; 
    /* above all elements, even if z-index is used elsewhere 
     it can be lowered as needed, but this value surpasses 
     all elements when used on YouTube for example. */ 
    z-index: 9999999999;    
    /* takes up 100% of page */ 
    width: 100%; height: 100%;   
    /* dim the page with 50% black background when visible */ 
    background-color: rgba(0,0,0,0.5); 
    /* a nice fade effect, visibility toggles after 175ms, opacity will animate for 175ms. note display:none cannot be animated. */ 
    transition: visibility 175ms, opacity 175ms; 
} 
</style> 
<!-- both visibility:hidden and display:none can be used, 
    but the former can be used in CSS animations --> 
<div style="visibility:hidden; opacity:0" class="dropzone"></div> 

Anche se la zona di lancio sarà copre l'intera pagina, utilizzando visibility:hidden o display:none nasconderà alla vista. Ho usato visibility:hidden in modo che le animazioni CSS possano essere utilizzate per animare la transizione.

Assegnazione degli eventi

<script> 
/* lastTarget is set first on dragenter, then 
    compared with during dragleave. */ 
var lastTarget = null; 

window.addEventListener("dragenter", function(e) 
{ 
    lastTarget = e.target; // cache the last target here 
    // unhide our dropzone overlay 
    document.querySelector(".dropzone").style.visibility = ""; 
    document.querySelector(".dropzone").style.opacity = 1; 
}); 

window.addEventListener("dragleave", function(e) 
{ 
    // this is the magic part. when leaving the window, 
    // e.target happens to be exactly what we want: what we cached 
    // at the start, the dropzone we dragged into. 
    // so..if dragleave target matches our cache, we hide the dropzone. 
    if(e.target === lastTarget || e.target === document) 
    { 
     document.querySelector(".dropzone").style.visibility = "hidden"; 
     document.querySelector(".dropzone").style.opacity = 0; 
    } 
}); 
</script> 

Quindi, ecco il processo: È possibile trascinare un file sulla finestra, e window.ondragenter incendi immediatamente. target è impostato sull'elemento principale, <html>. A questo punto, viene immediatamente visualizzata la tua dropzone, che copre l'intera pagina. window.ondragenter scatterà di nuovo, questa volta l'obiettivo sarà il tuo dropzone. Ogni volta che l'evento dragenter si attiva, memorizza nella cache il target, poiché questo sarà il target che corrisponderà all'ultimo evento window.ondragleave che viene generato quando si trascina fuori dalla finestra.

Perché funziona? Non ne ho idea, ma è così che si fa. Questo è praticamente l'unico metodo di lavoro che si attiva quando l'utente si allontana dalla pagina.

Credo che funzioni perché una volta che la casella di riepilogo non è nascosta, sarà sempre l' l'ultimo obiettivo. Copre ogni pixel della pagina, anche il tag <html>. Questo metodo si basa sull'attivazione del trascinamento di dragleave quando si esce dalla finestra. Sfortunatamente c'è un bug in Firefox che impedisce il corretto funzionamento.Si prega di votare per questo sarà risolto prima. A partire da Firefox 57.0.2, il dragleave sembra funzionare correttamente. Tuttavia, è necessaria una soluzione, controllando document invece che l'elemento memorizzato nella cache:

if(e.target === lastTarget || e.target === document) 

Here's a JSBin of it in action. Testato nelle ultime versioni di Chrome, Firefox, Edge e IE11.

+0

Questo non funziona in Firefox, in quanto il file event.target restituito da dragleave è HTML, che non corrisponde all'ultimo elemento memorizzato nella cache (molto probabilmente il .dropzone). –

+0

@DannyLin Sfortunatamente c'è un [bug in Firefox] (https://bugzilla.mozilla.org/show_bug.cgi?id=656164) che impedisce il corretto funzionamento. Si prega di votare per esso in modo che verrà risolto prima. – bryc

+0

Ah, quindi il bug sembra essere stato risolto ora? Firefox restituisce in modo affidabile 'document' come' event.target' quando lascia la finestra. Ho incluso questo workaround nella risposta. – bryc

Problemi correlati