Minule jsme si představili objektový model dokumentu, tedy datovou strukturu, kterou je reprezentován HTML dokument v prohlížeči
z pohledu skriptů. Dlužno podotknout, že se pohříchu jedná spíše jen o představu autorů norem z W3C:
v praxi je od těchto standardů stále ještě víc než dost odchylek - a čím starší prohlížeč, tím je od norem více vzdálen.
Teprve nové, moderní programy se normami začínají řídit, ale k nějakému univerzálnímu sjednocení se teprve pomalu přibližujeme.
Při tvorbě DHTML proto musíme stále myslet na různá "špecifiká", či lépe řečeno "špeky", které na
nás mají prohlížeče pochystané a které číhají i na těch nejnečekanějších místech.
Již víme z minulého dílu, že standard DOM
(který je podporován v moderních prohlížečích) pro nalezení nějakého prvku v dokumentu používá metodu document.getElementById()
(a další, viz poznámku níže) - zatímco starší prohlížeče používají jiné, vesměs navzájem nekompatibilní
techniky (document.all v MSIE, či document.ids v NN4). Ty jsme se už naučili obejít: vytvořili
jsme funkci objGet (resp. objNajdi), která postupně zkouší, kterou z metod prohlížeč zná, a použije tu vhodnou.
Ale ještě jsme si neřekli, co je vlastně hodnotou této funkce - čili jaký objekt vlastně dostaneme.
Tady narážíme na další kámen úrazu. Kdyby se totiž prohlížeče lišily pouze v rozdílných metodách, jak najít ten správný
objekt, nebyl by to zase takový problém. Jenže ony se liší i mnohem hlubšími rozdíly ve struktuře stromu dokumentu, především
v tom, jakými objekty jsou vlastně reprezentovány prvky stránky, jaká je jejich hierarchie a jaké jsou dostupné metody
pro manipulaci s těmito prvky.
Standardním objektem reprezentujícím prvky na stránce je v DOM objekt třídy Element. Tento objekt má
mnoho předdefinovaných vlastností i metod, s nimiž se postupně setkáme. V NN4 však prvky na stránce reprezentuje
jiný typ objektu - objekt třídy JSSTag. Ten má definovány zcela jiné vlastnosti a metody a s objektem Element
je zcela nekompatibilní. Největším a nejnepříjemnějším rozdílem mezi nimi z hlediska DHTML je hlavně to, že objekt Element
má definovánu standardní vlastnost Element.style, což je objekt, obsahující všechny vlastnosti CSS, které
lze na prvek aplikovat - zatímco v NN4 prvek JSSTag vlastnost style nemá a vlastnosti CSS (ne
však všechny) jsou uloženy přímo v tomto objektu. Např. CSS vlastnosti padding-top v prvním případě odpovídá
hodnota Element.style.paddingTop, zatímco v případě druhém je to JSSTag.paddingTop.
| typ prohlížeče |
metoda nalezení prvku |
výsledný typ objektu |
| DOM (MSIE5+, NN6+, Mozilla, Opera5+ atd.) |
document.getElementById(id_prvku) |
Element |
| MSIE od verze 4 |
document.all.id_prvku |
Element |
| NN verze 4.x |
document.ids.id_prvku |
JSSTag |
K tomu je ještě nutno poznamenat, že Netscape od verze 6 již zase ani metodu document.ids ani objekt JSSTag
nezná, je to tedy záležitost pouze Netscapu verzí 4.x. Microsoft zatím volí cestu dlouhodobější kompatibility, i jeho
nejnovější verze prohlížeče MSIE stále podporuje metodu document.all - je však jen otázkou času, kdy bude
podpora starých, nestandardních objektů všeobecně zrušena a zůstane jen u čistého DOM.
Pozn.: Připomeňme, že uvedené metody jsou jediné, kterými lze nějaký prvek na stránce nalézt
- jsou pouze nejběžnější a nejčastěji používané. Pro naše potřeby budou v tuto chvíli dostatečné. V některých situacích
je však vhodné použít jiný způsob vyhledání objektů. Několik ukázek bylo uvedeno v minulém
díle, s dalšími se setkáme v průběhu seriálu, včetně obecného traverzování dokumentu podle standardu DOM2.
Protože naším cílem je vytvořit univerzálně použitelnou knihovnu javascriptů pro DHTML, musíme se rozhodnout, jak se s
podobnými nekompatibilitami vypořádat. Možností je samozřejmě mnoho - namátkou:
- vytvořit více verzí knihovny - pro NN4, pro MSIE4, pro DOM atd. Do stránky potom načteme pouze příslušnou verzi podle
typu prohlížeče
- vytvořit více verzí "nebezpečných" funkcí a ve vlastních skriptech volat příslušnou funkci podle použitého
prohlížeče (např.
if(browser=="NN") obj=objGetNN(x); else obj=objGetDOM(x);
- vytvořit knihovnu pouze podle standardu DOM a prohlížeče, které mu nevyhovují, od skriptů "odříznout"
- vytvořit knihovnu co nejuniverzálnější s podporou pro co nejširší okruh prohlížečů
Kterou z variant zvolit a jaké jsou jejich výhody či nevýhody, je tématem na samostatný
článek - my zde použijeme poslední dvě z uvedených. Vytváříme dvě varianty knihovny - první DOM-kompatibilní, bude
ji možno použít pouze v prohlížečích podporujících DOM (alespoň v těch funkcích, které potřebujeme). Druhá pak bude univerzálnější
(tudíž subtilnější z hlediska funkčnosti a naopak robustěnější co do objemu). Ve druhém případě však musíme počítat s
tím, že různé prohlížeče budou naše objekty také různě interpretovat. Bude však postačující, když budeme pamatovat na
to, že naše funkce getObj() vrací objekt neurčitého typu (typu Element v DOM prohlížečích a
v MSIE, nebo typu JSSTag v NN4) a připravíme na to všechny ostatní funkce v knihovně. Budeme-li pak pro práci s
objekty používat pouze knihovní funkce, nebude nás typ objektu a problémy s nekompatibilitou ve svých skriptech
vůbec trápit. Při přípravě knihovny na to ale musíme pamatovat neustále.
Jméno nebo objekt?
V naší první funkci ještě provedeme jedno doplnění. Funkci pro nalezení prvku budeme volat často - vždy, když budeme chtít
pracovat s nějakým prvkem na stránce (což je skoro pokaždé). Při návrhu ostatních funkcí pracujících s prvky na stránce
máme vždy dvě možnosti - buďto jim budeme předávat již nalezený objekt (před každou akcí musíme zavolat funkci objGet()
a předat funkci nalezený objekt jako parametr); anebo můžeme pracovat se jmény (ID) prvků a v každé funkci pak prvek znovu
hledat. Jednoduchou úpravou je ale možné obě tyto možnosti spojit do jediné a umožnit tak všem funkcím, aby mohly jako
parametr dostat jak jméno prvku, tak i objekt, který nějaký prvek reprezentuje.
Pokud budeme předpokládat, že parametrem jakékoli funkce může být jak jméno prvku, tak objekt, postačí, když
nejprve otestujeme, o kterou z možností se jedná - pokud je parametrem jméno prvku, nalezneme odpovídající objekt, pokud
je jím objekt sám, nic nehledáme a pracujeme dále přímo s tímto objektem. Tento test nám může provést naše "hlavní"
funkce objGet(): zavoláme ji v každé další funkci hned na začátku a zajistíme, aby nám pokaždé vrátila příslušný
objekt - ať již jí předáme ID, nebo objekt samotný. Tím se vše sjednotí a v jednotlivých funkcích budeme dále pracovat
vždy jen s objekty.
Otestovat, jakého typu je parametr, který funkce dostala, je poměrně snadné. Protože jméno prvku (ID) je vždy řetězec,
budeme hledat prvek pouze tehdy, pokud parametr bude typu string (k tomu v JS slouží operátor typeof).
Jinak budeme předpokládat, že parametr je objektem. Funkci objGet() tedy doplníme - nejprve DOM verze:
// Webtip.cz - DHTML knihovna - DOM verze
function objGet(x) {
if (typeof x != 'string') return x;
else return document.getElementById(x);
}
var objNajdi = objGet;
a v "univerzální" verzi (od minule s menšími úpravami):
// Webtip.cz - DHTML knihovna - Univerzální verze
function objGet(x) {
if (typeof x != 'string') return x;
else if (Boolean(document.getElementById))
return document.getElementById(x);
else if (Boolean(document.all))
return eval('document.all.'+x);
else if (Boolean(document.ids))
return eval('document.ids.'+x);
else
return null;
}
var objNajdi = objGet;
Nyní již tedy můžeme funkci objGet předat jako parametr jak ID prvku, tak objekt - a máme zajištěno, že se
nám vždy vrátí objekt (pokud existuje - jinak se vrátí null). V univerzální verzi je výsledný objekt typu
Element, případně JSSTag - podle toho, v kterém prohlížeči funkci spustíme. Příklad:
function zmenBarvu(x,barva) {
var obj = objGet(x);
if (obj && obj.style) obj.style.color = barva;
}
var x = 'nadpis1';
zmenBarvu(x,'yellow');
var o = objNajdi('nadpis2');
zmenBarvu(o,'yellow');
V prvním případě voláme funkci zmenBarvu s parametrem typu řetězec (x='nadpis1') - objGet
zde vrátí objekt odpovídající prvku s id="nadpis1". Ve druhém případě voláme tuto funkci s již
nalezeným objektem - objGet zde zjistí, že jeho parametr tentokrát není typu řetězec a vrátí jej beze změny
zpátky; funkce tak opět pracuje s objektem (odpovídající prvku s id="nadpis2").
Ukázky k tomuto dílu:
Ukázka 1 - test funkce objGet()
Použitelnost prvků zmíněných v tomto díle:
typeof
- ECMAScript edition - 2, JavaScript - 1.1, JScript - 1.0, Internet Explorer - 3.02, Netscape - 3.0, Opera - 3.0
objekt Element
- DOM level - 1, JavaScript - 1.5, JScript - 3.0, Internet Explorer - 4.0, Netscape - 6.0
objekt JSSTag
- JavaScript - 1.2, Netscape - 4.0, dále nepodporováno
DHTML knihovna ke stažení:
| Číslo verze: |
0.3 (pro přehlednost budeme číslovat verze shodně s díly seriálu) |
| Stažení DOM verze: |
dhtml_0_3_dom.js |
| Obsah (anglické názvy) |
objGet() |
| Obsah (české názvy) |
objNajdi() |
| Stažení "univerzální" verze: |
dhtml_0_3_uni.js |
| Obsah (anglické názvy) |
objGet() |
| Obsah (české názvy) |
objNajdi() |
Staníček Petr
|