Questo post spiega le nozioni di base del trascinamento.
Creare contenuti trascinabili
Nella maggior parte dei browser, le selezioni di testo, le immagini e i link sono trascinabili per impostazione predefinita. Ad esempio, se trascini un link in una pagina web, vedrai una piccola casella con un titolo e un URL che puoi rilasciare sulla barra degli indirizzi o sul desktop per creare una scorciatoia o accedere al link. Per poter trascinare altri tipi di contenuti, devi usare le API di trascinamento di HTML5.
Per rendere un oggetto trascinabile, imposta draggable=true
su quell'elemento. Il trascinamento può essere eseguito praticamente su ogni elemento, ad esempio immagini, file, link, file o qualsiasi markup sulla pagina.
L'esempio seguente crea un'interfaccia per riorganizzare le colonne disposte con la griglia CSS. Il markup di base per le colonne ha il seguente aspetto, con l'attributo draggable
per ogni colonna impostato su true
:
<div class="container">
<div draggable="true" class="box">A</div>
<div draggable="true" class="box">B</div>
<div draggable="true" class="box">C</div>
</div>
Questo è il codice CSS per gli elementi contenitore e riquadro. L'unico CSS correlato alla funzionalità di trascinamento è la proprietà cursor: move
. Il resto del codice controlla il layout e lo stile degli elementi contenitore e riquadro.
.container {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
}
.box {
border: 3px solid #666;
background-color: #ddd;
border-radius: .5em;
padding: 10px;
cursor: move;
}
A questo punto puoi trascinare gli elementi, ma non succede nient'altro. Per aggiungere un comportamento, devi utilizzare l'API JavaScript.
Ascolta il trascinamento degli eventi
Per monitorare la procedura di trascinamento, puoi rimanere in ascolto dei seguenti eventi:
Per gestire il flusso di trascinamento, ti servono un tipo di elemento di origine (il punto in cui inizia il trascinamento), il payload dei dati (l'elemento che viene trascinato) e una destinazione (un'area in cui eseguire il trascinamento). L'elemento di origine può essere praticamente qualsiasi tipo di elemento. Il target è la zona di rilascio o l'insieme di zone di rilascio che accetta i dati che l'utente sta cercando di eliminare. Non tutti gli elementi possono essere target. Ad esempio, il tuo target non può essere un'immagine.
Inizio e fine di una sequenza di trascinamento
Dopo aver definito gli attributi draggable="true"
sui contenuti, aggiungi un gestore di eventi dragstart
per avviare la sequenza di trascinamento per ogni colonna.
Questo codice imposta l'opacità della colonna al 40% quando l'utente inizia a trascinarla, poi riportala al 100% quando termina l'evento di trascinamento.
function handleDragStart(e) {
this.style.opacity = '0.4';
}
function handleDragEnd(e) {
this.style.opacity = '1';
}
let items = document.querySelectorAll('.container .box');
items.forEach(function (item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragend', handleDragEnd);
});
Il risultato può essere visto nella seguente demo di Glitch. Trascina un elemento e la sua
opacità cambia. Poiché l'elemento di origine contiene l'evento dragstart
, impostare
this.style.opacity
su 40% fornisce un feedback visivo all'utente per capire che questo elemento è
la selezione corrente che viene spostata. Quando elimini l'elemento, l'elemento di origine restituisce un'opacità del 100%, anche se non hai ancora definito il comportamento di rilascio.
Aggiungi altri indizi visivi
Per aiutare l'utente a capire come interagire con la tua interfaccia, utilizza i gestori di eventi dragenter
, dragover
e dragleave
. In questo esempio, le colonne sono target degli elementi selezionati e possono essere trascinate. Aiuta l'utente a capirlo tratteggiando il bordo quando tiene un elemento trascinato sopra una colonna. Ad esempio, nel tuo CSS, potresti creare una classe over
per gli elementi che sono destinazioni target:
.box.over {
border: 3px dotted #666;
}
Quindi, nel codice JavaScript, imposta i gestori di eventi, aggiungi la classe over
quando
la colonna viene trascinata e rimuovila quando esce l'elemento trascinato. Nel
gestore dragend
ci assicuriamo inoltre di rimuovere le classi alla fine
del trascinamento.
document.addEventListener('DOMContentLoaded', (event) => {
function handleDragStart(e) {
this.style.opacity = '0.4';
}
function handleDragEnd(e) {
this.style.opacity = '1';
items.forEach(function (item) {
item.classList.remove('over');
});
}
function handleDragOver(e) {
e.preventDefault();
return false;
}
function handleDragEnter(e) {
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over');
}
let items = document.querySelectorAll('.container .box');
items.forEach(function(item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragover', handleDragOver);
item.addEventListener('dragenter', handleDragEnter);
item.addEventListener('dragleave', handleDragLeave);
item.addEventListener('dragend', handleDragEnd);
item.addEventListener('drop', handleDrop);
});
});
Ci sono un paio di punti che vale la pena trattare in questo codice:
L'azione predefinita per l'evento
dragover
è impostare la proprietàdataTransfer.dropEffect
su"none"
. La proprietàdropEffect
verrà trattata più avanti in questa pagina. Per il momento, tieni presente che impedisce l'attivazione dell'eventodrop
. Per eseguire l'override di questo comportamento, chiamae.preventDefault()
. Un'altra buona prassi è restituirefalse
nello stesso gestore.Il gestore di eventi
dragenter
viene utilizzato per attivare/disattivare la classeover
anzichédragover
. Se utilizzidragover
, l'evento si attiva ripetutamente mentre l'utente mantenendo l'elemento trascinato sopra una colonna, causando l'attivazione ripetuta della classe CSS. In questo modo il browser esegue molte operazioni di rendering non necessarie, il che può influire sull'esperienza utente. Ti consigliamo vivamente di ridurre al minimo i ridisegnati e, se devi utilizzaredragover
, valuta la possibilità di limitare o eliminare il listener di eventi.
Completa il lancio
Per elaborare il rilascio, aggiungi un listener di eventi per l'evento drop
. Nel gestore drop
devi impedire il comportamento predefinito del browser per i rilasci, che in genere sono una sorta di fastidioso reindirizzamento. Per farlo, chiama il numero e.stopPropagation()
.
function handleDrop(e) {
e.stopPropagation(); // stops the browser from redirecting.
return false;
}
Assicurati di registrare il nuovo gestore insieme agli altri gestori:
let items = document.querySelectorAll('.container .box');
items.forEach(function(item) {
item.addEventListener('dragstart', handleDragStart);
item.addEventListener('dragover', handleDragOver);
item.addEventListener('dragenter', handleDragEnter);
item.addEventListener('dragleave', handleDragLeave);
item.addEventListener('dragend', handleDragEnd);
item.addEventListener('drop', handleDrop);
});
Se esegui il codice in questo punto, l'elemento non viene spostato nella nuova posizione. A tal fine, utilizza l'oggetto DataTransfer
.
La proprietà dataTransfer
contiene i dati inviati in un'azione di trascinamento. dataTransfer
viene impostato nell'evento dragstart
e letto o gestito nell'evento di rilascio. La chiamata a
e.dataTransfer.setData(mimeType, dataPayload)
consente di impostare il tipo MIME
e il payload dei dati dell'oggetto.
In questo esempio, consentiremo agli utenti di riorganizzare l'ordine delle colonne. A tale scopo, devi prima memorizzare il codice HTML dell'elemento di origine all'avvio del trascinamento:
function handleDragStart(e) {
this.style.opacity = '0.4';
dragSrcEl = this;
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', this.innerHTML);
}
Nell'evento drop
, elabori la riduzione di colonna impostando l'HTML della colonna di origine sull'HTML della colonna di destinazione in cui hai rilasciato i dati. Questo include anche controllare che l'utente non torni nella stessa colonna da cui è stato trascinato.
function handleDrop(e) {
e.stopPropagation();
if (dragSrcEl !== this) {
dragSrcEl.innerHTML = this.innerHTML;
this.innerHTML = e.dataTransfer.getData('text/html');
}
return false;
}
Puoi vedere il risultato nella demo seguente. Affinché questa operazione funzioni, ti servirà un browser desktop. L'API Drag and Drop non è supportata sui dispositivi mobili. Trascina e rilascia la colonna A sopra la colonna B e osserva come cambia la posizione:
Altre proprietà di trascinamento
L'oggetto dataTransfer
espone le proprietà per fornire un feedback visivo all'utente durante il processo di trascinamento e per controllare il modo in cui ogni destinazione di rilascio risponde a un determinato tipo di dati.
dataTransfer.effectAllowed
limita il "tipo di trascinamento" che l'utente può eseguire sull'elemento. Viene utilizzato nel modello di elaborazione mediante trascinamento per inizializzaredropEffect
durante gli eventidragenter
edragover
. La proprietà può avere i seguenti valori:none
,copy
,copyLink
,copyMove
,link
,linkMove
,move
,all
euninitialized
.dataTransfer.dropEffect
controlla il feedback che l'utente riceve durante gli eventidragenter
edragover
. Quando l'utente mantiene il puntatore su un elemento target, il cursore del browser indica il tipo di operazione che verrà eseguita, ad esempio una copia o uno spostamento. L'effetto può assumere uno dei seguenti valori:none
,copy
,link
,move
.e.dataTransfer.setDragImage(imgElement, x, y)
significa che, invece di utilizzare il feedback predefinito del browser relativo all'immagine fantasma, puoi impostare un'icona di trascinamento.
Caricamento file
In questo semplice esempio viene utilizzata una colonna sia come origine del trascinamento sia come destinazione. Ciò può accadere in una UI che chiede all'utente di riorganizzare gli elementi. In alcuni casi, la destinazione e l'origine del trascinamento potrebbero essere tipi di elementi diversi, ad esempio in un'interfaccia in cui l'utente deve selezionare un'immagine come immagine principale di un prodotto trascinando l'immagine selezionata su una destinazione.
Il trascinamento viene spesso utilizzato per consentire agli utenti di trascinare elementi dal desktop in un'applicazione. La differenza principale è nel tuo gestore drop
. Anziché utilizzare dataTransfer.getData()
per accedere ai file, i suoi dati vengono contenuti nella proprietà dataTransfer.files
:
function handleDrop(e) {
e.stopPropagation(); // Stops some browsers from redirecting.
e.preventDefault();
var files = e.dataTransfer.files;
for (var i = 0, f; (f = files[i]); i++) {
// Read the File objects in this FileList.
}
}
Puoi trovare ulteriori informazioni in merito nella sezione Trascinamento personalizzato.