Esistono, nascosti dietro le luci sfavillanti di HTML5 alcuni tesori nascosti, trattati separatamente e senza intorno il clamore suscitato da altre features.
Stiamo parlando del meccanismo di Drag&Drop nativo e dei microdata; iniziamo dal primo (il cui draft trovate a questo indirizzo): la ben nota ( sin da quando vennero sviluppate le APIs per IE5) operazione di Drag&Drop è descritta nel documento come un evento mousedown seguito da una serie di eventi mousemove. Leggendo le specifiche ho trovato alcuni punti piuttosto confusi ma cercherò come sempre fatto in questa guida di isolare le informazioni certe e chiare.
Il documento indica come rendere un elemento draggabile, per raggiungere lo scopo sono necessarie due semplici operazioni:
- aggiungere all’elemento un attributo draggable.
- associare un event listener per l’evento dragstart che si occupi di salvare i dati relativi all’ operazione in un oggetto DataTransfer.
Aggiungerei che un elemento draggable dovrebbe possedere una proprietà CSS "cursor: move;", in modo che sia visivamente chiaro che l’elemento è "trascinabile".
L’attributo draggable
Tutti gli elementi HTML possono avere un attributo draggable, che è una enumeration e consiste in tre stati:
- state = true: l’elemento è trascinabile.
- state = false: l’elemento non è trascinabile
- state = auto (default); utilizza il comportamento di default dello user-agent.
Il getter/setter è nella forma: element.draggable [ = value ] dove value è ovviamente booleano (true o false).
L’attributo dropzone
Ciò che è trascinabile spesso è previsto abbia una zona di "deposito", la dropzone.
Tutti gli elementi HTML possono rappresentare una "dropzone". Quando l’attributo è specificato deve consistere in una lista di tokens separati da spazi, i valori previsti sono:
- copy: indica che il drop di un elemento risulterà in una copia del draggable.
- move: indica che il drop di un elemento risulterà in una move del draggable nella nuova locazione.
- link: indica che il drop di un elemento risulterà in un collegamento ai dati originali.
- una keyword di lunghezza >= ad 8 caratteri che inizi con "string:".
- una keyword di lunghezza >= a 6 caratteri che inizi con "file:".
I valori di keyword specificano il tipo di dato accettato, ad esempio "string:text/plain", "file:image/png".
L’attributo non può contenere più di uno tra i valori copy, move e link. Il default è copy.
Ecco un esempio base riportato nel draft e riadattato che definisce un’area di deposito ed alcuni elementi draggable:
<script>
function dragStartHandler(event) {
...
}
function dropHandler(event) {
...
}
</script>
<p>Lista prodotti</p>
<ol ondragstart="dragStartHandler(event)">
<li draggable="true" data-value="TV">Televisione fullHD</li>
<li draggable="true" data-value="Tablet">Tablet Android</li>
<li draggable="true" data-value="Mobile">Mobile UMTS</li>
</ol>
<p>Trascina i prodotti nel carrello:</p>
<ol dropzone="move string:text/x-example" ondrop="dropHandler(event)"></ol>
Il drag data store
I dati che viaggiano nel contesto di una operazione di Drag&Drop consistono in una collezione di informazioni conosciute come "drag data store":
la drag data store item list, in cui ogni item è composto da:
- un "drag data item kind": Plain Unicode String oppure File.
- un "drag data item type": generalmente il MIME type ma può essere una stringa qualsiasi.
- il dato vero e proprio (actual data)
La lista è ordinata per ordine di inserimento così come in una coda FIFO (First In First Out).
Fanno parte del drag data store anche:
- l’informazione del feedback di default dello user-agent (drag data store default feedback).
- una lista di uno o più elementi definita come drag data store elements list.
- opzionalmente una immagine bitmap e le coordinate di un punto dell’immagine stessa (rispettivamente drag data store bitmap e drag data store hot spot coordinate).
- un drag data store mode che può valere
- Read/write (evento dragstart, nuovi dati possono essere aggiunti al drag data store)
- Read-only (evento drop, nessun nuovo dato può essere aggiunto al drag data store)
- Protected (ogni altro evento, i formati ed i tipi presenti nella drag data store item list possono essere enumerati, ma gli actual data non sono disponibili, inoltre nessun nuovo dato può essere aggiunto)
- un "drag data store allowed effects state", stringa.
Prima di produrre del codice funzionante affrontiamo l’analisi delle APIs, facendo conoscenza con le interfacce coinvolte.
DataTransfer
Signature
interface DataTransfer {
attribute DOMString dropEffect;
attribute DOMString effectAllowed;
readonly attribute DataTransferItemList items;
void setDragImage(Element image, long x, long y);
void addElement(Element element);
/* old interface */
readonly attribute DOMStringList types;
DOMString getData(DOMString format);
void setData(DOMString format, DOMString data);
void clearData(optional DOMString format);
readonly attribute FileList files;
};
Gli oggetti DataTransfer sono utilizzati all’interno degli eventi legati al Drag&Drop, e sono validi fintanto che gli eventi sono in corso.
Analizziamo uno per uno attributi e metodi dell’oggetto:
- dataTransfer.dropEffect [ = value ]: getter/setter per il tipo di operazione selezionato (none, copy, link, move).
- dataTransfer.effectAllowed [ = value ]: getter/setter per i tipi di operazione consentiti, possibili valori: "none", "copy", "copyLink", "copyMove", "link", "linkMove", "move", "all", "uninitialized".
- dataTransfer.items: getter per la lista di item (oggetto DataTransferItemList).
- dataTransfer.setDragImage(element, x, y): utilizza element per effettuare un update del drag feedback, rimpiazzando feedback specificati in precedenza.
- dataTransfer.addElement(element): aggiunge element alla lista di elementi usati per il drag feedback.
- dataTransfer.types: ritorna una DOMStringList, un elenco in buona sostanza dei formati impostati nell’evento dragstart; se un file dovesse essere stato trascinato, tra i tipi verrà inclusa la stringa Files.
- data = dataTransfer.getData(format): ritorna il dato specificato, se non esistesse ritornebbe la stringa vuota.
- dataTransfer.setData(format, data): aggiunge i dati specificati.
- dataTransfer.clearData([ format ]): rimuove i dati del formato specificato (o tutti qualora format non fosse indicato).
- dataTransfer.files: ritorna un oggetto FileList contenente i file trascinati, qualora vi fossero.
DataTransferItemList
Signature
interface DataTransferItemList {
readonly attribute unsigned long length;
getter DataTransferItem (unsigned long index);
deleter void (unsigned long index);
void clear();
DataTransferItem? add(DOMString data, DOMString type);
DataTransferItem? add(File data);
};
Vediamo nel dettaglio i vari campi e metodi:
- items.length: getter che restituisce il numero di items nel drag data store.
- items[index]: getter per lo specifico item la cui posizione è descritta da index.
- delete items[index]: rimuove l’item all’indice index all’interno del drag data store.
- items.clear(): rimuove tutti gli items.
- items.add(data, [type]): aggiunge una nuova entry, il parametro type deve essere fornito in caso in cui il dato sia plain text.
DataTransferItem
Signature
interface DataTransferItem {
readonly attribute DOMString kind;
readonly attribute DOMString type;
void getAsString(FunctionStringCallback? callback);
File? getAsFile();
};
[Callback, NoInterfaceObject]
interface FunctionStringCallback {
void handleEvent(DOMString data);
};
Analizziamo attributi e metodi:
- item.kind: getter per il "drag data item kind", può valere quindi "string:" o "file:".
- item.type: getter per il drag data item type
- item.getAsString(callback): per item kind = "Plain Unicode string", invoca il callback con la stringa come argomento.
- file = item.getAsFile(): ritorna un oggetto File (per item kind = "file:").
DragEvent
Signature
[Constructor(DOMString type, optional DragEventInit eventInitDict)]
interface DragEvent : MouseEvent {
readonly attribute DataTransfer? dataTransfer;
};
dictionary DragEventInit : MouseEventInit {
DataTransfer? dataTransfer;
};
Possiede un solo membro dataTransfer, accessibile tramite event.dataTransfer.