DHTML pro každého - 3. díl: Objektový model v praxi

Upoutávka

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:

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()

 


Petr Staníček, pixy.cz © 2002, 2015