Interval.cz
Programujeme DHTML aplikace û drag&drop systΘm

Tentokrßt se spoleΦn∞ pustφme do klasickΘ ·lohy, kterou by si m∞l ka₧d² kodΘr pokusit sßm napsat. Ka₧d², kdo ji₧ n∞co podobnΘho psal, Φi brouzdal internetem po n∞jakΘm vytvo°enΘm zdroji, vφ, ₧e asi neexistuje identick² k≤d. Co programßtor, to jin² p°φstup. UrΦit∞ i p°i studiu mΘho skriptu zjistφte proΦ nejde tohle jinak atd. Proto bych rßd p°edeslal, ₧e se jednß pouze o nßvrh, kter² si jist∞ dle sv²ch pot°eb upravφte.

Nejprve vÜak musφm zaΦφt milou povinnostφ. Jak mnozφ z vßs jist∞ post°ehli, na sv∞te je dalÜφ verze Opery. AΦ se jednß o beta verzi, i ta naznaΦila mnoho pozitivnφho. Pro mne je tφm nejv∞tÜφm krokem vp°ed mnohem v∞tÜφ podpora DOM. Pravda, nenφ kompletnφ, ale mnoh²m skript∙m dostaΦujφcφ. Jak jsem v diskusi nad n∞kter²m z minul²ch dφl∙ prohlßsil, tφm jedin²m d∙vodem, proΦ jsem Operu vylouΦil z tohoto serißlu, byla jejφ chyb∞jφcφ DOM implementace.

Tento ale ji₧ argument padß, proto je nunΘ nejprve zm∞nit skript pro rozpoznßvßnφ prohlφ₧eΦ∙ sniffer.js. Jednak p°ibylo rozpoznßnφ Opery 7.0, dßle pak rozliÜenφ verzφ MSIE 5.5, 5 a 6. To proto, neb n∞kterΘ drobnΘ rozdφly mohou vadit a bylo by na Ükodu vÜechny MSIE hßzet do jednoho pytle. Na obranu proti mo₧nΘ faleÜnΘ identifikaci Opery byla zp°φsn∞na kritΘria pro p°i°azenφ jednotliv²ch prom∞nn²ch (IE a NS), pouhß identifikace nßzvu a zßkladnφch metod ji₧ nestaΦily.

// Identifikace OPERY 7
if(ua.indexOf('opera')>-1 && document.getElementById && document.childNodes) {OPERA=true;}
....
// Identifikace OPERY 7 s falesnym oznacovanim
if(!IE && !NS && !OPERA && document.addEventListener) {OPERA=true;}

Ale zp∞t k nosnΘmu tΘmatu toho Φlßnku. Nejprve co budeme k realizaci pot°ebovat. Jak vÜichni vφte, drag&drop je vlastn∞ uchycenφ n∞jakΘho objektu, ta₧enφ na jinou pozici a upuÜt∞nφ na cφlovΘ pozici, vÜe pomocφ kursoru. Mφsφ se zde udßlosti, pozicovßnφ a prßce se strukturou dokumentu. Ne₧ zaΦnu popisovat svΘ myÜlenkovΘ pochody, podφvejte se na jednoduchou ukßzku, vyu₧φvajφcφ zmi≥ovanou knihovnu sniffer.js a popisovanou knihovnu dragdrop.js.

Inicißtorem ka₧dΘho pokusu o p°esunutφ objektu je stisknutφ tlaΦφtka myÜi nad dan²m elementem. Tuto udßlost obslou₧φ metoda m_move. Ne₧ se k nφ ale dostanu, zaΦnu nejd°φve procedurou, umo₧≥ujφcφ elementu b²t p°etahovßn. V tΘto implementaci je oznaΦenφ °eÜeno konkrΘtnφ konstrukcφ stargDrag, je₧ objektu (parametr what) p°i°adφ reakci na stisk tlaΦφtka v podob∞ volßnφ zmi≥ovanΘ metody m_down:

function startDrag(what)
{
  what.onmousedown = function(e) {if(e) {event=e;} m_down(event,what);}
  what.drag = 1;
}

function stopDrag(what)
{
  what.onmousedown = null;
  what.drag = 0;
}

Pouze nad oznaΦen²mi elementy se tedy vyvolß obsluhujφcφ funkce m_down se vstupujφcφm parametrem objektu, kter² prßv∞ obsluhuje (parametr object) a vyvolanΘ udßlosti (parametr event). Mo₧nß se setkßte se zßpisem, p°i°azujφcφ celΘmu dokumentu reakci na ka₧d² stisk. Jen₧e v takovΘmto p°φpad∞ je t°eba slo₧it∞ji zjiÜ¥ovat, kter² objekt vlastn∞ obsluhuje udßlost stisku a zda-li je v∙bec urΦen k p°etahovßnφ. K oznaΦovßnφ se pak obvykle zneu₧φvß atribut class, co₧ se mi osobn∞ nezamlouvß, neb se drag&drop omezuje jen na konkrΘtnφ t°φdu. UrΦit²m v²chodiskem by bylo pou₧itφ atributu name Φi zcela jinΘho smyÜlenΘho atributu. To ale zase odmφtne validßtor.

Jakmile je nad oznaΦen²m objektem kliknuto myÜφ, je to signßl, ₧e by m∞l b²t p°etahovßn, a je zavolßna metoda m_down. Ta ve svΘm t∞le zaznamenß p°edevÜφm aktußlnφ pozici objektu (atributy top a left) a myÜi (atributy clickX a clickY) v dob∞ stisku a dßle pov∞sφ na pohyb myÜi proceduru m_move. Jak²koli pohyb tedy bude obsluhovßn touto metodou.

dragobj.clickX = event.clientX;
dragobj.clickY = event.clientY;
dragobj.left = parseInt(dragobj.style.left);
dragobj.top = parseInt(dragobj.style.top);

if(IE)
{
 document.attachEvent('onmousemove', m_move);
 document.attachEvent('onmouseup', m_up);
}

if(NS || OPERA)
{
 document.addEventListener('mousemove', m_move, true);
 document.addEventListener('mouseup', m_up, true);
}

Jak jsem p°edeslal, a₧ do chvφle, kdy sejmeme prst z tlaΦφtka, se p°i ka₧dΘm pohybu volß m_move. Ta je ve svΘ podstat∞ jen jak²si prost°ednφk, neb nerealizuje samotn² p°esun, ale pouze poΦφtß rozdφl mezi pozici myÜi v okam₧iku poΦßtku p°esunu a v jeho pr∙b∞hu. Dan² rozdφl se pak p°edß p°φmo metod∞ danΘho objektu, nesoucφ povinn² nßzev ondragdrop, kterß pak podle pot°eby provede samotnou zm∞nu pozice p°iΦtenφm vypoΦφtanΘho rozdφlu k p∙vodnφ pozici objektu. Zase se jist∞ setkßte s tφm, ₧e se p°esun zajiÜ¥uje ji₧ na ·rovni m_move. Ostatn∞ vÜe pot°ebnΘ je k dispozici. Pokud ale budete pot°ebovat s p°esunem dßle pracovat a ka₧dΘmu objektu jej budete chtφt m∞nit, nap°φklad omezovat na urΦitΘ osy nebo rozmezφ, p∙jde to jen st∞₧φ.

function m_move(event)
{
 // Spocti rozdil mezi puvodni pozici mysi a nynejsi pozici
 dragobj.ondragdrop(event.clientX-dragobj.clickX,event.clientY-dragobj.clickY);
 return stopEvent(event);
}

Uvoln∞nφ tlaΦφtka logicky znamenß, ₧e mß u₧ivatel p°etahovßnφ dost a je t°eba ukonΦit p°esun a hlavn∞ odstranit ovladaΦe udßlosti pohybu myÜi. Pro mo₧nou rozÜi°itelnost je op∞t volßna metoda ondragend, abychom mohli jeÜt∞ po skonΦenφ provΘst n∞jakou volitelnou akci.

function m_up(event)
{
if(IE)
{
 document.detachEvent('onmousemove', m_move);
 document.detachEvent('onmouseup', m_up);
 if(dragobj.ondragend)dragobj.ondragend(event);
}
if(NS || OPERA)
{
 document.removeEventListener('mousemove', m_move, true);
 document.removeEventListener('mouseup', m_up, true);
 if(dragobj.ondragend)dragobj.ondragend(event);
}

To byl samotn² systΘm knihovny dragdrop.js. Implementace je velmi jednoduchß a modifikovatelnß dle pot°eb. Nezapome≥te vlo₧it knihovny sniffer.js a dragdrop.js. V druhΘ fßzi pak ka₧dΘmu objektu definujte "p°esouvacφ" metodu ondragdrop. Nakonec p°esun aktivovujte volßnφm startDrag s parametrem souvisejφcφho objektu, nap°φklad takto:

document.getElementById('box1').ondragdrop = function(posunx, posuny, event)
{
 this.style.left = (this.left + posunx)+"px";
 this.style.top = (this.top + posuny)+"px";
}

startDrag(document.getElementById('box1'));

Na zßv∞r bych v krßtkosti uvedl dva mφrn∞ pokroΦilejÜφ p°φklady. Ten p∙vodnφ se ji₧ objevil na Intervalu a jeho ekvivalent, vytvo°en² pomocφ tΘto knihovny, najdete v p°φloze. Zmi≥uji jej proto, abych nßzorn∞ ukßzal, jak jednoduÜe lze drag&drop pro jednotlivΘ objekty specifikovat. V tomto p°φpad∞ je pot°eba omezit jednu osu a rozsah, respektive provΘst diskretizaci.

document.getElementById('p').ondragdrop = function(posunx,posuny)
{
 temp = (this.left + posunx);

 // Presun pouze o kazdych 18 pixelu
 temp -= temp%18;

 // Omezeni rozsahu
 if(temp<0) temp=0;
 if(temp>34) temp=34;
 this.style.left = temp+"px";
}

Druh² p°φklad vychßzφ ze stßle populßrn∞jÜφho nastavovßnφ vlastnφho barevnΘho schΘmatu webu a ukazuje, jak by bylo mo₧nΘ umo₧nit u₧ivateli interaktivn∞ definovat vlastnφ barevnΘ kombinace. VÜechny soubory pou₧itΘ v tomto Φlßnku naleznete p°ipravenΘ ke sta₧enφ v souboru dhtml6.zip



Michal Kus²n (28.12. 2002)

Redakce Interval.cz |  Inzerce na Interval.cz |  Hledßme novΘ autory ISSN 1212-8651 
 ⌐ Zoner software, s.r.o., vÜechna prßva vyhrazena, tento server dodr₧uje prßvnφ p°edpisy o ochran∞ osobnφch ·daj∙.