Categorie
Javascript Programmazione

{JS} DOM API: il Drag and drop

Risorse sul web:
https://web.dev/drag-and-drop/
https://www.html.it/pag/15203/eventi-legati-al-trascinamento-del-mouse/

Il DnD

Con il Drag and Drop (DnD) possiamo trascinare quasi tutti gli elementi di una pagina da una posizione ad un altra. E’ opportuno precisare che la maggior parte dei browser rende per impostazione predefinita, trascinabili, le selezioni di testo, le immagini ed i collegamenti.

Rendere un elemento trascinabile

E’ possibile rendere un oggetto trascinabile impostando l’attributo con valore true – draggable=true – su quell’elemento

<div draggable="true" class="box">A</div>

Oggetti coinvolti nel DnD

Esistono tre termini che identificano gli oggetti coinvolti nel DnD:

sourceIdentifica l’elemento che verrà trascinato e che quindi subirà il trascinamento
data
payload
Identifica cosa spostare dell’elemento trascinabile; se l’elemento stesso o ad esempio il valore di un suo attributo
targetIdentifica la posizione dove è possibile rilasciare l’elemento trascinato o i dati dall’elemento source

I tre eventi principali di un DnD

dragstartEvento che si verifica all’inizio del trascinamento di un elementoSi applica all’elemento source
dragoverE’ il momento prima del rilascio quando ci posizioniamo sull’elemento target senza ancora rilasciarlo; di fatto abilita un elemento a fungere da elemento targetSi applica all’elemento target
dropEvento che si scatena al momento del rilascio effettivo dell’ elementoSi applica all’elemento target

Il DnD é composto principalmente da 3 eventi:

  • dragstart è il momento in cui si clicca sull’elemento source senza rilasciare il tasto del mouse e si inizia il trascinamento;
  • dragover é il momento in cui, sempre tenendo ancora premuto il tasto del mouse, ci si posiziona sull’elemento target ma senza ancora rilasciarlo;
  • drop, in ultimo, è il momento in cui si rilascia il tasto del mouse sull’elemento target e determina la fine del trascinamento ed il rilascio effettivo dell’elemento source o dei dati a esso estratti.

Esempio esplicativo

HTML

<div id="uno" class="block">
   <p id="paragrafo" data-description="Interessante" 
   draggable="true">Testo</p>
</div>

<div id="due" class="block">
</div>

Nel codice html abbiamo inserito due div entrambi hanno un id univoco e la medesima classe per applicarli dello stile condiviso; All’interno del primo div è stato inserito un paragrafo con gli attributi: id, data-* e draggable.

Nota: I data-* global attributes formano una classe di attributi chiamati attributi 
di dati personalizzati , che consentono lo scambio di informazioni proprietarie tra l' HTML e la sua rappresentazione DOM tramite script.

CSS

.block {
float:left;
border: 1px solid black;
height: 150px;
width: 300px;
} 

Nel codice css abbiamo migliorato visivamente i due div, che condividono la stessa classe, affiancandoli con la proprietà float, aggiungendogli una bordatura e definendone la dimensione.

JS

dragstart

var elementoTarget;
elementoTarget = document.getElementById("paragrafo");
elementoTarget.addEventListenet('dragstart', function(event) {
	event.dataTransfer.setData("text", event.target.getAttribute('data-description'));
}

Nella prime due righe di codice abbiamo registrato la posizione dell’elemento source, ovvero, del paragrafo di cui vogliamo inizializzare il trascinamento; Nelle seguenti righe applichiamo, allo stesso elemento, l’evento dragstart. Nella funzione, che prevede l’oggetto autogenerato event, particolare attenzione è da porre all’istruzione dataTransfer.setData(format, data).

data Payload
.setData

Si tratta infatti del Data Payload, ovvero, della proprietà dataTransfer dell’oggetto event del DnD e del primo dei suoi due metodi: setData.

Con la seguente istruzione: event.dataTransfer.setData(“text”, event.target.getAttribute(‘data-description’));

stiamo comunicando di:

prendere il valore dell’attributo data-description, indicandogli che si tratta di un valore di tipo “testo” e che appartiene all’oggetto target dell’evento dragstart. Stiamo, di fatto, chiedendo di tenere tale dato in memoria (provvisoria) sino al termine del DnD.

Nota:
Se avessimo voluto trasferire l'intero elemento nella nuova posizione avremmo potuto scrivere cosi:

event.dataTransfer.setData("text/html", event.target.innerHTML);

Stiamo leggendo e memorizzando l'intero codice html dell'elemento 

È altresì importante precisare che è possibile richiamare i valori degli attributi, attraverso le shorthand, solo se essi sono attributi standard dell'elemento selezionato.

event.target.id     
corretto

event.target.type    

errato, in quanto type è un attributo riservato agli elementi di tipo input e non paragrafo; sarà quindi restituito undefined. Potremmo però utilizzare, come visto precedentemente, getAttribute(). 

Risorse dal web:

https://webplatform.github.io/docs/dom/DataTransfer/setData/#format

https://it.javascript.info/dom-attributes-and-properties

dragover

var elementoTarget;
elementoTarget=document.getElementById("due");
elementoTarget.addEventListener('dragover', function(event) {
	event.target.className = "blockOpacity";
	event.preventDefault();
}

Nelle prime due righe di codice abbiamo memorizzato la posizione dell’elemento target e attraverso l’associazione dell’evento dragover stiamo di fatto abilitando esso a fungere come posizione di destinazione.

.blockOpacity {
border: 1px dotted yellow;
height: 150px;
width: 300px;
opacity: 50%;
} 

event.target.className = “blockOpacity”;

Nel contempo ci siamo preoccupati di cambiargli classe in modo che quando siamo su di esso posizionati il div assumerà un bordo dotted di colore giallo ed una trasparenza al 50%.

event.preventDefault();

Abbiamo in ultimo disabilitiamo il comportamento predefinito del browser. Quest’ultima operazione è fondamentale per l’abilitazione dell’elemento target, altrimenti il browser non consentirebbe il rilascio (drop) dell’elemento trascinato.

drop

elementoTarget.addEventListener('drop', function(event) {
	var data = "<p> Valore attributo data-description:" + event.dataTransfer.getData("text") + "</p>";
event.target.appendChild(data);
}

In ultimo associando all’elemento target l’evento drop prepariamo esso alla fase conclusivo di rilascio.

Data payload

.getData

Con il secondo metodo getData della proprietà dataTransfer ci occupiamo di recuperare l’informazione precedentemente memorizzata. Lo facciamo racchiudendola in una variabile “data” che passeremo come argomento al metodo appendChild().

Altri eventi per il DnD

EventoDescrizione
dragentersi verifica quando l’elemento trascinato entra nell’area occupata dall’elemento target
dragleavesi verifica quando l’elemento trascinato lascia l’area occupata dall’elemento target
dragendsi verifica quando l’elemento trascinato è stato rilasciato

Con questi altri eventi è possibile personalizzare maggiormente l’operazione creando ad esempio effetti grafici.

DnD di più elementi

HTML

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

CSS

.container {
  float:left;
}

.box {
  border: 3px solid #666;
  background-color: #ddd;
  border-radius: .5em;
  padding: 10px;
  cursor: move;
}

In questo caso abbiamo un div contenitore con all’interno altre tre div da poter riposizionare a piacimento. Come di consueto abbiamo migliorato la loro visualizzazione con regole css.

JS

function handleDragStart(event) {
  event.dataTransfer.setData("text/html", 
  event.target.innerHTML;
}

function handleDragOver(event) {
	event.preventDefault();
}

function  handleDrop(event) {
  var data = event.dataTransfer.getData("text/html");
  event.target.appendChild(data);
}

Per facilitarci abbiamo definito tre funzioni per i tre eventi, che includono il parametro event, esternamente al successivo blocco di codice in modo da poterle richiamare e semplificare la lettura.

var items = document.querySelectorAll('.container .box');
  items.forEach(function(item) {
  item.addEventListener('dragstart', handleDragStart);
    item.addEventListener('dragover', handleDragOver);
    item.addEventListener('drop', handleDrop);

Attraverso querySelectorAll() otteniamo un array di tutti gli elementi che condividono le classi container e box.

Fruttando il metodo forEach() applichiamo ad ogni elemento indistintamente i tre eventi fondamentali per il DnD.

DnD per riordinare gli elementi nelle colonne

In questo esempio, consentiremo agli utenti di riorganizzare l’ordine delle colonne. Per fare questo dobbiamo prima memorizzare l’HTML dell’elemento sorgente quando inizia il trascinamento

function handleDragStart(event) {
  dragSrcEl = this;
  event.dataTransfer.setData('text/html', 
  this.innerHTML);
}

Nel dropcaso in cui si elabori il rilascio della colonna, impostare l’HTML della colonna di origine sull’HTML della colonna di destinazione su cui è stato rilasciato, verificando prima che l’utente non ritorni sulla stessa colonna da cui è stato trascinato.

function handleDrop(event) {
  event.stopPropagation();

  if (dragSrcEl !== this) {
    dragSrcEl.innerHTML = this.innerHTML;
    this.innerHTML = 
    event.dataTransfer.getData('text/html');
  }

  return false;
}

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *