V dnešním díle se ještě zaměříme na problematické aspekty objektů reprezentujících prvky dokumentu, konflikty mezi prohlížeči a ukážeme si, jak je v praxi vyřešit nebo se jim aspoň vyhnout.
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:
if(browser=="NN") obj=objGetNN(x); else obj=objGetDOM(x);
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.
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ázka 1 - test funkce objGet()
typeof
objekt Element
objekt JSSTag
Čí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() |
Petr Staníček, pixy.cz © 2002, 2015