Programujeme DHTML aplikace – dokument

9. října 2002

Po nultém seznamovacím díle se pustíme do konkrétních situací a začneme se zabývat elementy dokumentu. Jak mnozí tuší, bude řeč především o objektovém modelu dokumentu, který je alfou a omegou veškerých DHTML aplikací.

Document Object Model (DOM) je standardizované rozhraní mezi vámi a prohlížečem načteným dokumentem. Nebudu vás zatěžovat podrobnými technickými specifikacemi, ostatně několik zmínek již padlo minule. Snad jen to, že jej vyvíjí skupina W3C a existuje několik vývojových stupňů číslovaných od 1 do 3, přičemž každá z nich obsahuje rozdílnou sadu modulů pro práci s dokumentem.

Fakticky je funkce DOMu od toho, aby vám pomocí vlastního API s bohatou kolekcí vlastností a metod umožnil pracovat s kompletním obsahem dokumentu od HTML elementů, atributů až po externí stylové šablony. Jak je z názvu patrné, jde o objektovou reprezentaci stránky, přestože ta je ve své podstatě lineárním textovým zápisem. Samotné uspořádání objektů ovšem není náhodné, nýbrž má svá přísná pravidla. DOM, respektive prohlížeč který jej podporuje, při načítaní vytváří stromovou hierarchii prvků, podle toho jak jsou v HTML zápise stránky jednotlivé elementy poskládány. Představme si symbolický kus HTML zápisu a nazvěme jej html_zapis_1.


Reprezentace úrovní HTML a DOM

Jak vidíte na obrázku, DOM interpretuje HTML zápis jako systém podřízených a nadřízených prvků ve stromové struktuře podle toho, jak jsou do sebe elementy vloženy. Důležité je také odlišení jednotlivých pojmů. Zatímco elementem dokumentu míníme jakýkoli prvek (např. span) pracující na úrovni HTML, z hlediska DOM je tento objekt reprezentován uzlem (anglicky node), jež je podle zmiňovaných pravidel zařazen do jisté struktury uzlů dokumentu. Ovšem je důležité si uvědomit, že ačkoli mohou být pojmy uzel a element funkčně totožné, není to totéž. Každý element je na úrovni DOM uzlem, ale naopak rovnost vždy neplatí, neboť uzel může být i jiného typu!

K práci s jednotlivými uzly existuje ve specifikaci DOM tzv. uzlové rozhraní, což je sbírka vlastností příslušící danému typu uzlu. Rozhraní jsou utvořena také strukturovaně, což znamená, že je nejprve definováno nejvyšší Node, jež je dále specifikováno dalšími „podrozhraními“. Celá struktura je velmi obsáhlá, proto ji nebudu uvádět, navíc ji najdete v originálních dokumentech W3C. Dnes budeme pracovat s rozhraním Document a HTMLDocument pracující s dokumentem jako celkem a základním Node. Tím nejdůležitějším ale není podrobná znalost všech metod (dokumentace je tady pro tento účel:-), nýbrž základní orientace ve struktuře uzlů a pravidlech používání příslušných rozhraní. Proto budu popisovat pouze základní metody, včetně aplikace na html_zapis_1. Ostatní metody jsou povětšinou funkčně podobné a nechávám na čtenáři, aby si je sám odzkoušel.

Tip:

Otestuje si v dokumentu implement.htm, jaké moduly standardu W3C DOM úrovní 1, 2, 3 podporuje váš prohlížeč. Výsledky ale berte s rezervou, neboť false neznamená, že je daný modul prohlížeči úplně cizí, ale že jeho podpora není 100% (pouze pro IE6, Mozilla0.9.1+, NS6+).

Přístup k elementům a uzlům

Abychom mohli s elementem provádět jakékoli operace, musíme jej nejprve v dokumentu lokalizovat, což znamená získat na něj referenci (odkaz). Způsobů je celá řada, ovšem podotýkám, že se řídí standardy, tudíž proprietální řešení tentokrát vynechám. Při lokalizaci uzlu pracujeme s  dokumentem jako takovým, proto použijeme metody rozhraní Document a HTMLDocument. Tím nejjednodušším a nejrozšířenějším postupem je přiřadit pří zápisu kódu HTML elementu svoji unikátní identifikaci (atribut ID) a poté využít funkci getElementById(id), která podle parametru id vrátí referenci na daný element. Chceme-li vybrat typově stejnou skupinu elementů podle názvu tagu, jejich reference lze získat pomocí metody getElementsByTagName(tagName).

Vybrané metody rozhraní HTMLDocument a jejích aplikace na html_zapis_1:

getElementById()
Zápis: document.getElementById('div1');
Vrací: referenci na element span s attributem id='div1';

getElementsByTagName()
Zápis: document.getElementsByTagName('span');
Vrací: pole s referencemi na všechny elementy dokumentu typu span

Občas tyto metody nelze použít (například kvůli chybějícímu atributu ID) nebo je efektivnější využít procedur pracujících přímo s již zmiňovanou stromovou hierarchií uzlů. Bohužel neexistuje žádná exaktně definovaná proměnná, která by ji popisovala, a tak se k navigaci využívá jednotlivých uzlů a rozhraní Node. Každý uzel má definované své příbuzné uzly. Například ChildNodes obsahuje pole referencí na podřízené uzly, nazývající se děti, dceřiné prvky nebo potomci. ParentNode je reference na nadřazený uzel, kterému se říká rodič a NextSibling je reference na následující uzel stejné úrovně, jež se nazývá sourozenec. Pro ilustraci jsou v tabulce uvedeny základní „rodinné“ vlastnosti, ostatní jsou funkčně podobné.

Vybrané metody rozhraní Node a jejích aplikace na html_zapis_1:

childNodes[]
Zápis: document.getElementById('div1').childNodes;
Vrací: pole s referencemi na 2 elementy span s atributy id='text1' a id='text1';
Vysvětlení: dětmi uzlu div1 jsou uzly text1 a text2

parentNode
Zápis: document.getElementById('text1').parentNode;
Vrací: referenci na element div s atributem id='div1'
Vysvětlení: rodičem uzlu text1 je uzel div1

nextSibling
Zápis: document.getElementById('text1').nextSibling;
Vrací: referenci na element span s atributem id='text2'
Vysvětlení: dalším sourozencem uzlu text1 je uzel text2

Atributy

Teď, když už víte, jak získat referenci na libovolný uzel, můžeme začít manipulovat s atributy elementu, jehož je uzel reprezentant. Tím ovšem nemyslím vlastnosti popsané v rozhraní, ale klasické HTML atributy (např. href u tagu a). K přístupu k atributům, případně jejich změně existuje docela hodně možností, záleží na dané situaci, jaký zvolíme. Z Javaskriptu znáte klasický tečkový operátor, takže například atribut name nějakého elementu lze získat konstrukcí element.name. Nepopiratelná výhoda je jednoduchost zápisu, ovšem smazává se rozdíl mezi atributy elementu a vlastnostmi uzlu. U zápisu element.href a element.firstChild na první pohled nerozlišíte co je vlastnost HTML a co DOM.

Standart W3 DOM LEVEL 1 definuje další cesty k atributům, ovšem zde se plně projevuje rozdílnosti mezi jednotlivými prohlížeči. Rozhraní Node zná pojem attributes, což není nic jiného, než seznam všech atributů daného elementu. O tom, že jich má každý element ve vínku požehnaně, se majitelé Explorerů mohou přesvědčit v dokumentu atributy.htm. Před tímto použitím bych se osobně vyvaroval, neboť tento prvek prodělal u každé verze IE proměnu a tak například u IE5.0 se jedná o pole jednotlivých hodnot u IE6 pole objektů typu atribut. Aby toho nebylo málo, Mozilla jako jediná dodržuje standard. U ní se tedy jedná o specifické „pole“ typu NamedNodeMap, jež se nechová jako klasická repetice. Navíc seznam a počet všech atributů je u každé verze jiný. Snad proto je specifikován uzel typu Element, jehož rozhraní disponuje metodami k tomuto přímo určenými a jsou funkční u všech prohlížečů stejně. Jejich seznam naleznete v popisu. Ještě bych rád dodal, že ačkoli existuje metoda createAttribute, obecně platí, že pokud není atribut definován (i při obyčejném přiřazení – např.pomocí setAttribute), bude automaticky vytvořen.

Speciálním případem je atribut class, u něhož nelze používat tečkovou konvencí element.class. Je nutno použít buďto zápis element.className nebo element.getAttribute('className').

Vybrané metody pro přístup k atributům a jejich aplikace na html_zapis_1:

tečkový operátor
Zápis: document.getElementById('div1').id;
Vrací: řetězec ‚div1‘

attributes[]
Zápis: document.getElementById('text1').attributes['id'];
Vrací: řetězec ‚tex1‘

getAttribute()
Zápis: document.getElementById('text2').getAttribute('id');
Vrací: řetězec ‚text2‘

Tip:

Zdají se vám zápisy příliš dlouhé? Obvyklým trikem, jak ulehčit prstům a neopisovat dlouhé názvy procedur, je vytvoření zkratek v podobě nových funkcí, například takto :
function objekt(what) {return document.getElementById(what);}
function deti(what) {return document.getElementById(what).childNodes;}

Procházení dokumentem

Abychom trochu upustili od teorie a ukázali si některé tyto metody v praxi, doporučuji prostudovat a vyzkoušet klasický příklad na procvičení základní orientace v systému uzlů a elementů. Pokuste se tedy napsat jednoduchý skript, který, využívaje dnes uvedených metod, vypíše strukturu všech elementů dokumentu tak, jak je vidět z pohledu DOM. Pro ty, kteří by si nevěděli rady, jsou k dispozici dvě verze. Ta první v dokumentu struktura.htm je jednoduší a princip spočívá v získání všech elementů na stránce pomocí jedné funkce (víte které?) a následném formátování. Pro naši úlohu je to řešení dostačující, ale není odpovídající. Výhodnější a využívanější je pohybovat se přímo po stromu uzlů. Jako návod vám snad poslouží návrh zápisu „průchozí“ funkce použité v dokumentu struktura2.htm. V podstatě jde pouze o metodu, která cyklem projde všechny děti uzlu vstupujícího jako parametr start a provede žádanou akci (například výpis názvu). Rekurzivním voláním této funkce i pro tyto děti se zajistí průchod celým podstromem. Upozorňuji na klíčové slovo var použité v definici smyčky. Bez něj by docházelo k přepisování proměnné x během rekurze a výpis by nebyl funkční.

/// Pruchozi funkce
function projdi(start)
{
  for(var x=0; x < start.childNodes.length; ++x)
  {
   alert(‚Objekt id:‘ + start.childNodes[x].id);
   projdi(start.childNodes[x])
  }
}
// použítí například pro celý dokument
projdi(document);

Na první podhled tedy stejně vypadající výpisy, ale pokud se podíváte pozorněji, uvidíte zásadní změnu, kterou je zobrazení všech uzlů, tedy i typu Text. Navíc tím, že procházíme strom rekurzí, známe i aktuální hloubku, což je pro skript užitečné a nemusí se vnoření dodatečně zjišťovat. Speciálně bych pak doporučil podívat se, jak se ve struktuře projeví odřádkování dvou sousedních elementů (v tomto případě span).

Tip:

Pokud chcete získat seznam všech HTML elementů (nikoli uzlů) v dokumentu, vyzkoušejte zápis document.getElementsByTagName('*'), který je ekvivalentní se známým, ale nestandardním document.all

Všechny soubory použité v tomto díle a další příklady si mužete najednou stáhnout v souboru dhtml2.zip

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *