Webdesignérův průvodce po HTML5: Táhni a srůstej

Čtenář jistě promine kulturní narážku v nadpisu. Samosebou jde o implementaci metody drag-and-drop, tedy česky „táhni a pusť“. Podpora pro tuto metodu je totiž zahrnuta již do specifikace HTML5, takže už není potřeba k těmto účelům využívat JavaScriptových knihoven (i když se JavaScriptu nevyhneme).
Seriál: Webdesignérův průvodce po HTML5 (21 dílů)
- Webdesignérův průvodce po HTML5 – díl nultý 25. 5. 2010
- Webdesignérův průvodce po HTML5 – nová sémantika 1. 6. 2010
- Webdesignérův průvodce po HTML5 – nová sémantika II 8. 6. 2010
- Webdesignérův průvodce po HTML5 – pohyblivé obrázky 15. 6. 2010
- Webdesignérův průvodce po HTML5 – používáme pohyblivé obrázky 22. 6. 2010
- Webdesignérův průvodce po HTML5 – taháme data od návštěvníka 29. 6. 2010
- HTML5 Audio: rádio ve vašich stránkách 13. 7. 2010
- Webdesignérův průvodce po HTML5: Microdata 20. 7. 2010
- AppCache: webové aplikace i bez připojení 27. 7. 2010
- Webdesignérův průvodce po HTML5: WebStorage 3. 8. 2010
- Webdesignérův průvodce po HTML5: Multithreading s WebWorkers 10. 8. 2010
- Webdesignérův průvodce po HTML5: Databáze v prohlížečích 17. 8. 2010
- Webdesignérův průvodce po HTML5: Shrnutí a rozhrnutí 24. 8. 2010
- HTML5: ukládáme si data k elementům 6. 12. 2010
- Webdesignérův průvodce po HTML5: Táhni a srůstej 5. 1. 2011
- HTML5: První krůčky s FileSystem API 15. 2. 2011
- Mobilizujeme web v HTML5 4. 4. 2011
- Single Page Apps a řešení problémů s historií 1. 6. 2011
- Page Visibility API: Kouká na mě vůbec někdo? 10. 8. 2011
- Práce se soubory v prohlížeči, díl 1 15. 8. 2011
- Práce se soubory v prohlížeči, díl 2 5. 9. 2011
Nálepky:
Technika drag-n-drop (Drag&Drop, táhni a pusť) je jednou ze základních metod práce s objekty v grafických UI, kdy pomocí vstupního zařízení je objekt „uchopen“, pak „přenesen“ na jiné místo a tam „položen“ či „vložen“, dle kontextu. Není divu, že webové aplikace mají potřebu tuto techniku implementovat taky, a specifikace HTML5 jim v tomto vychází vstříc.
Základy
Pokud chceme něco táhnout a pustit, musíme mít ve stránce dva elementy:
- ten, který se bude přesouvat (draggable)
- ten, nad kterým bude element puštěn (container)
Netřeba v tom hledat žádné složitosti – jak tažený objekt, tak kontejner může být obyčejný div.
Draggable
Elementy, které budou přetahovány, musí mít nastavený atribut draggable
na hodnotu true. Zde poznámka: Při testech nestačilo pouhé uvedení atributu (jak umožňuje HTML) ani nastavení např. na hodnotu 1 či „on“, bylo třeba zapsat „draggable=true“. Pokud má element nastaven draggable atribut, bude možné jej uchopit a táhnout. Bez něho se bude při podobném pokusu zvýrazňovat text.
<div id=draggable1 draggable=true>Prvek 1</div>
Elementy, které lze přetahovat, budou vyvolávat i události, konkrétně dragstart a dragend.
dragstart
Událost „dragstart“ je vyvolána ve chvíli, kdy uživatel zahájí tažení objektu. V obsluze této události se nejčastěji nastavuje obsah („náklad“, dataTransfer) daného elementu, může se nastavit i „zástupce“, který bude zobrazen během přetahování a nastavují se např. i povolené operace (kopírování, přesun, vytvoření odkazu…) Obsluha události může celou operaci zrušit, pokud vrátí hodnotu false.
dragend
Pokud skončí tažení, ať už puštěním nebo zrušením, je vyvolána událost dragend. Obsluha by měla uklidit (odstranit dataTransfer).
Container
Kontejner, tedy oblast, do níž jsou přetahované objekty „vhazovány“, nemusí mít žádný speciální atribut (před krátkým časem se v návrhu spcifikace objevil atribut „dropzone“, ale je ve stádiu „first draw“). Stačí pouze řádně obsloužit příslušné události.
dragenter
Událost je vyvolána, pokud uživatel přetáhne objekt nad aktuální element. Není nutné ji implementovat.
dragover
Událost je vyvolávána, dokud se objekt pohybuje nad elementem. Pokud obsluha vrací true, prohlížeč naznačí uživateli, že zde nelze objekt upustit. Pokud obslužná rutina vrátí false, lze objekt pustit. V obsluze této metody je možné nastavit např. způsob přetažení (copy / move / link) apod.
drop
Ukončuje celou operaci přetažení. V obsluze této události je potřeba zpracovat celou operaci a udělat potřebné (přesunout objekt na nové místo, vyvolat akci apod.)
Posloupnost událostí při úspěšné operaci přetažení je následující:
- dragstart @ draggable
- dragenter @ container
- dragover @ container
- drop @ container
- dragend @ draggable
Ukázka
Ukažme si jednoduché demo. Začneme vytvořením tří oblastí:
<!DOCTYPE html> <meta charset=utf-8> <html> <head> <title>Drag'n'Drop Demo</title> </head> <body> <h1>Drag'n'Drop Demo</h1> <div id=container1 ondragover="return over(event)" ondrop="return drop(event)"> <h2>Zásobník</h2> </div> <div id=container2 ondragover="return over(event)" ondrop="return drop(event)"> <h2>Sudá</h2> </div> <div id=container3 ondragover="return over(event)" ondrop="return drop(event)"> <h2>Lichá</h2> </div> <div id=container> <h2>Konzole</h2> <textarea id=log></textarea> </div> </body> </html>
Vytvořili jsme si tři oblasti – kontejnery (plus jednu testovací, kde se bude vypisovat log). Do první oblasti („zásobníku“) si umístíme objekty, které budeme přetahovat:
<div id=container1 ondragover="return over(event)" ondrop="return drop(event)"> <h2>Zásobník</h2> <div id=draggable1 draggable=true ondragstart="return start(event)" ondragend="return end(event)">Prvek 1</div> <div id=draggable2 draggable=true ondragstart="return start(event)" ondragend="return end(event)">Prvek 2</div> <div id=draggable3 draggable=true ondragstart="return start(event)" ondragend="return end(event)">Prvek 3</div> <div id=draggable4 draggable=true ondragstart="return start(event)" ondragend="return end(event)">Prvek 4</div> </div>
Objekty mají nastavenou obsluhu událostí dragstart
i dragend
a mají atribut draggable
.
Než se dostaneme k vlastnímu oživení, tak si elementy trošku nastylujme:
<style> #container1, #container2, #container3, #container { float:left; width:250px; height:550px; padding:10px; margin:10px; background-color: #cff;} #draggable1, #draggable2, #draggable3, #draggable4, #draggable { width:75px; height:70px; padding:5px; margin:5px;background-color: #ffc;} #log {width:230px;height:400px;} </style>
Obsluha událostí
Aby dávalo přetahování nějaký smysl, je třeba nastavit obsluhu patřičných událostí. My si v příkladu ukážeme i využití objektu dataTransfer
, pomocí něhož můžeme dát přetahovanému elementu nějaký „vnitřní“ obsah – můžeme nastavit jeho reprezentaci v různých formátech, podobně jako např. u systémové schránky.
Ondragstart
function start(e) { e.dataTransfer.effectAllowed='all'; e.dataTransfer.setData("text/plain", e.target.getAttribute('id')); e.dataTransfer.setDragImage(e.target, 10, 50); return true; }
První řádek obsluhy události říká, že přetahovaný objekt lze kopírovat, přesouvat i vytvářet zástupce ( effectAllowed='all'
). Druhý řádek nastavuje reprezentaci objektu pro kontejnery, které jsou schopné přijmout prostý text (text/plain). Hodnota bude rovna id příslušného elementu. Pokud si tedy objekt přetáhneme do textarea (nebo třeba do textového editoru), získáme řetězec, např. „draggable1“. Analogicky bychom mohli nastavit obsah pro „text/html“, a ten by byl použit tam, kde lze vkládat HTML (rich text editory apod.) či jakýkoli jiný typ obsahu (obrázek, soubor, URL, …)
Třetí řádek nastavuje „obrázek“, který bude symbolizovat objekt během tažení. Zde je to objekt sám. Jak vidíte, lze nastavit i bod, v němž bude kurzor (to jsou ty dvě čísla).
Vracíme true
, operace přetahování je povolena.
Ondragend
function end(e) { e.dataTransfer.clearData("text/plain"); return true; }
Po skončení přesunu uklidíme – tedy smažeme obsah v objektu dataTransfer a vrátíme true – vše dopadlo dobře. Ve Firefoxu to bohužel nedopadne dobře – vrátí chybu, protože ve FF nelze s objektem dataTransfer manipulovat jinde než v ondragstart.
Ondragover
function over(e) { var iddraggable = e.dataTransfer.getData("text/plain"); var id = e.target.getAttribute('id'); if (id =='container1') {e.preventDefault();return false;} if(id =='container2' && (iddraggable == 'draggable2' || iddraggable == 'draggable4')) {e.preventDefault();return false;} else if (id =='container3' && (iddraggable == 'draggable1' || iddraggable =='draggable3')) {e.preventDefault();return false;} else return true; }
Obsluha události dragover
je vyvolávána, jak jsme si řekli, po dobu co se objekt pohybuje nad kontejnerem. Identifikátor taženého objektu zjistíme z textové reprezentace (viz výše, uložili jsme si jej tam v dragstart). Identifikátor kontejneru zjistíme z cíle události ( event.target
).
Pomocí jednoduchých pravidel pak rozhodneme o tom, zda povolíme puštění. Nad kontejnerem 1 přijmeme vše, takže jen vrátíme false (zde false
znamená, že je možno pustit, viz výše!) Nad kontejnerem 2 přijmeme jen objekty 2 a 4, nad trojkou jen 1 a 3 (čistě jako ukázku možností, nemá to žádný hlubší význam). Tam, kde je povoleno puštění objektu, vrátíme false, jinak true. Pro jistotu ještě zabráníme vyvolání výchozí obsluhy (mělo by stačit prosté return false, ale podle některých uživatelů jsou prohlížeče, v nichž to nestačí).
Ondrop
function drop(e) { e.stopPropagation(); e.preventDefault(); var iddraggable = e.dataTransfer.getData("text/plain"); e.target.appendChild (document.getElementById(iddraggable)); return false; }
Puštění objektu nejprve zabrání vyvolání výchozí obsluze událostí. Zjistíme si ID přetaženého elementu (máme ho v dataTransfer) a daný element přesuneme do cílové oblasti.
V ukázce je v rámci obsluhy události drop
ještě výpis objektu dataTransfer do logovacího okna.
Shrnutí
Drag and drop nalezne své využití v mnoha webových aplikacích a pomůže zvýšit míru jejich integrace se zbytkem systému. Díky nativní podpoře lze např. přetahovat objekty (soubory) z pracovní plochy systému do „drop kontejnerů“ na webové stránce.
Ahoj,
1/ HTML neumožňuje zadat pouze draggable, specifikace jasně říká, že draggable je enumerated atribut (true, false, auto)
2/ draggable netřeba specifikovat v případě elementů „img“ a „a“ (pokud má href)
3/ uvedené demo funguje pouze ve FF, nikoliv v Chrome, je to způsobenou chybou v implementaci ze strany FF, HTML5 vyžaduje, aby dragenter byl implementován a vracel false, pokud má být akce povolena
http://www.webnt.cz/4-drag-drop/
Problém bude pravděpodobně někde jinde, protože z localhost vše funguje bez problémů. Když jsem pátral po podrobnostech, zjistil jsem, že při spuštění z localhost v Chrome vrací getData(„text/plain“) správný obsah, ale při načtení téhož souboru ze serveru je tam „undefined“. Je to teď na serveru s výpisem do console.log, můžete si zkusit… Každopádně pátráme dál :)
Jak to máte v localhostu? pouštíte to z lokálního web serveru, nebo jako file://? Skoušeli jste implementovat i tu metodu, co jsem psal? Jinak si to budu muset stáhnout a testnout sám :)
Teď BFU dotaz , jak do konzole dostat místo tý spletě příkazů jednoduchý log o tom „kam jsem co natahal“ (pro případné uložení výsledku) Děkuji . Prosím nekamenovat :-)