In questo articolo mostreremo l'implementazione di una dashboard minimale in cui i widget aggiunti possono essere trascinati sulla griglia visuale.
La teoria
Per essere trascinabile tramite il drag and drop nativo un elemento deve avere l'attributo draggable
impostato su true
.
Quando il trascinamento inizia, viene innescato l'evento dragstart
. Va subito chiarito un concetto fondamentale: questo evento, e gli altri che vedremo, vengono gestiti sull'elemento genitore dell'elemento trascinato.
Quindi event.target
contiene un riferimento DOM all'elemento che viene trascinato. Nella fase iniziale del trascinamento possiamo marcare questo elemento con una classe CSS speciale che poi rimuoveremo una volta terminato il trascinamento. Gli eventi interessati sono:
dragstart
: l'operazione di trascinamento ha inizio sull'elemento scelto a cui possiamo accedere tramite la proprietà target dell'oggettoevent
;event.dataTransfer.effectAllowed
ci permette di specificare l'effetto visuale che verrà usato.dragover
: si innesca quando l'elemento scelto si trova sopra un altro elemento.dragend
: si innesca quando il trascinamento è terminato.drop
: si innesca quando l'elemento scelto viene rilasciato.
Tuttavia, questi eventi controllano solo le fasi del trasferimento e del rilascio, e per far si che l'elemento scelto venga spostato nell'area di destinazione a livello DOM, dobbiamo:
- Marcare l'elemento scelto con una classe CSS speciale quando l'operazione ha inizio.
- Ignorare o bloccare gli eventi intermedi.
- Sull'evento
drop
, selezionare l'elemento scelto, clonarlo e aggiungerlo al contenitore di destinazione, rimuovendolo dal DOM all'interno della stessa operazione. Poichè l'elemento clonato ha ancora la classe CSS speciale, dobbiamo rimuoverla in modo da avere un unico elemento per operazione.
Il codice
function dragWidgets(wrapper = null) {
if (!wrapper) {
return;
}
const containers = wrapper.querySelectorAll('.dashboard-content-area');
wrapper.addEventListener('dragstart', (e) => {
if (e.target && e.target.classList.contains('dashboard-widget')) {
e.target.classList.add('dragged');
e.dataTransfer.effectAllowed = 'move';
}
});
for (const container of containers) {
container.addEventListener('dragend', (e) => {
e.preventDefault();
});
container.addEventListener('dragover', (e) => {
e.preventDefault();
});
container.addEventListener('drop', (e) => {
const draggedEl = document.querySelector('.dragged');
if (draggedEl) {
const copy = draggedEl.cloneNode(true);
copy.classList.remove('dragged');
container.appendChild(copy);
draggedEl.remove();
}
e.preventDefault();
});
}
}
Demo
Conclusione
Questo tipo di layout ci permette di applicare il drag and drop nativo di JavaScript per implementare una funzionalità principale della GUI.