ZÃskejme trochu hlubšà náhled na DOM uzly.
V této kapitole se dozvÃme vÃc o tom, jak vypadajÃ, a nauÄÃme se jejich nejpoužÃvanÄjšà vlastnosti.
TÅÃdy DOM uzlů
Různé DOM uzly mohou mÃt různé vlastnosti. NapÅÃklad elementový uzel odpovÃdajÃcà znaÄce <a> má vlastnosti týkajÃcà se odkazu, uzel odpovÃdajÃcà znaÄce <input> má vlastnosti týkajÃcà se vstupu a podobnÄ. Textové uzly nejsou stejné jako elementové, ale i mezi nimi vÅ¡emi existujà spoleÄné vlastnosti a metody, jelikož vÅ¡echny tÅÃdy DOM uzlů tvoÅà jednu hierarchii.
Každý DOM uzel náležà do pÅÃsluÅ¡né vestavÄné tÅÃdy.
KoÅenem hierarchie je tÅÃda EventTarget. Z nà je zdÄdÄna tÅÃda Node a z nà jsou zdÄdÄny ostatnà DOM uzly.
Zde je obrázek, vysvÄtlenà bude následovat:
TÅÃdy jsou:
-
EventTarget â je koÅenová âabstraktnÃâ tÅÃda pro vÅ¡echno.
Objekty této tÅÃdy nejsou nikdy vytvoÅeny. TÅÃda sloužà jako báze, takže vÅ¡echny DOM uzly podporujà tzv. âudálostiâ. Prostudujeme je pozdÄji.
-
Node â je také âabstraktnÃâ tÅÃda, která sloužà jako báze pro DOM uzly.
Poskytuje jádro stromové funkcionality:
parentNode,nextSibling,childNodesa podobnÄ (to jsou gettery). Objekty tÅÃdyNodenejsou nikdy vytvoÅeny, ale existujà dalšà tÅÃdy, které jsou z nà zdÄdÄny (a tedy dÄdà funkcionalitu tÅÃdyNode). -
Document, z historických důvodů je z nÄj Äasto zdÄdÄn
HTMLDocument(aÄkoli nejnovÄjšà specifikace to nenaÅizuje) â je dokument jako celek.PÅÃmo do této tÅÃdy patÅà globálnà objekt
document, který sloužà jako vstupnà bod DOMu. -
CharacterData â âabstraktnÃâ tÅÃda, z nÞ jsou zdÄdÄny:
-
Element â je základnà tÅÃdou pro DOM elementy.
Poskytuje navigaci na úrovni elementů, napÅ.
nextElementSiblingnebochildren, a vyhledávacà metody, napÅ.getElementsByTagName,querySelector.ProhlÞeÄ podporuje nejen HTML, ale také XML a SVG. TÅÃda
Elementtedy sloužà jako báze pro specifiÄtÄjšà tÅÃdy:SVGElement,XMLElement(ty zde nebudeme potÅebovat) aHTMLElement. -
A koneÄnÄ HTMLElement je základnà tÅÃda pro vÅ¡echny HTML elementy. S nà budeme pracovat vÄtÅ¡inu Äasu.
Jsou z nà zdÄdÄny konkrétnà HTML elementy:
- HTMLInputElement â tÅÃda pro elementy
<input>, - HTMLBodyElement â tÅÃda pro elementy
<body>, - HTMLAnchorElement â tÅÃda pro elementy
<a>, - â¦a podobnÄ.
- HTMLInputElement â tÅÃda pro elementy
Existuje mnoho dalÅ¡Ãch znaÄek s vlastnÃmi tÅÃdami, které mohou mÃt specifické vlastnosti a metody. Naproti tomu nÄkteré elementy, napÅÃklad <span>, <section> nebo <article>, nemajà žádné specifické vlastnosti, takže jsou instancemi tÅÃdy HTMLElement.
Celá sada vlastnostà a metod každého uzlu je tedy výsledkem ÅetÄzce dÄdiÄnostÃ.
Uvažujme napÅÃklad DOM objekt pro element <input>. Ten patÅà do tÅÃdy HTMLInputElement.
Jeho vlastnosti a metody jsou sjednocenÃm následujÃcÃch (vyjmenovány v poÅadà dÄdiÄnosti):
HTMLInputElementâ tato tÅÃda poskytuje vlastnosti specifické pro vstup,HTMLElementâ poskytuje metody (a gettery/settery) spoleÄné pro HTML elementy,Elementâ poskytuje generické metody elementů,Nodeâ poskytuje vlastnosti spoleÄné pro DOM uzly,EventTargetâ poskytuje podporu událostà (budou vysvÄtleny v dalÅ¡Ãch kapitolách),- â¦a nakonec je vÅ¡e zdÄdÄno z tÅÃdy
Object, takže k dispozici jsou i metody âplaného objektuâ, napÅ.hasOwnProperty.
Chceme-li vidÄt název tÅÃdy DOM uzlu, můžeme si vzpomenout, že objekt zpravidla obsahuje vlastnost constructor. Ta se odkazuje na tÅÃdnà konstruktor a jeho název je v constructor.name:
alert( document.body.constructor.name ); // HTMLBodyElement
â¦Nebo můžeme prostÄ použÃt toString:
alert( document.body ); // [object HTMLBodyElement]
Můžeme také ovÄÅit dÄdiÄnost pomocà instanceof:
alert( document.body instanceof HTMLBodyElement ); // true
alert( document.body instanceof HTMLElement ); // true
alert( document.body instanceof Element ); // true
alert( document.body instanceof Node ); // true
alert( document.body instanceof EventTarget ); // true
Jak vidÃme, DOM uzly jsou obvyklé JavaScriptové objekty a k dÄdiÄnosti využÃvajà tÅÃdy založené na prototypech.
To je dobÅe vidÄt i pÅi vypsánà elementu v prohlÞeÄi pÅÃkazem console.dir(elem). V konzoli pak uvidÃte HTMLElement.prototype, Element.prototype a tak dále.
console.dir(elem) oproti console.log(elem)VÄtÅ¡ina prohlÞeÄů ve svých vývojáÅských nástrojÃch podporuje dva pÅÃkazy: console.log a console.dir. Oba vypÚà své argumenty na konzoli. Pro JavaScriptové objekty obvykle oba pÅÃkazy zobrazà totéž.
Pro DOM elementy se vÅ¡ak liÅ¡Ã:
console.log(elem)zobrazà DOM strom elementu.console.dir(elem)zobrazà element jako DOM objekt, což je dobré pro prozkoumánà jeho vlastnostÃ.
Zkuste si to na document.body.
Ve specifikaci nejsou DOM tÅÃdy popsány v JavaScriptu, ale ve speciálnÃm jazyce pro popis rozhranà nazvaném Interface description language (IDL), kterému je obvykle snadné porozumÄt.
V IDL je pÅed každou vlastnostà uveden jejà typ, napÅÃklad DOMString, boolean a podobnÄ.
UvádÃme úryvek z popisu s komentáÅi:
// Definice HTMLInputElement
// DvojteÄka ":" znamená, že HTMLInputElement je zdÄdÄn z HTMLElement
interface HTMLInputElement: HTMLElement {
// sem pÅijdou vlastnosti a metody elementů <input>
// "DOMString" znamená, že hodnotou vlastnosti je ÅetÄzec
attribute DOMString accept;
attribute DOMString alt;
attribute DOMString autocomplete;
attribute DOMString value;
// vlastnost s booleovskou hodnotou (true/false)
attribute boolean autofocus;
...
// teÄ metoda: "void" znamená, že metoda nevracà žádnou hodnotu
void select();
...
}
Vlastnost ânodeTypeâ
Vlastnost nodeType poskytuje jeÅ¡tÄ jeden, âstaromódnÃâ způsob, jak zÃskat âtypâ DOM uzlu.
Obsahuje ÄÃselnou hodnotu:
elem.nodeType == 1pro elementové uzly,elem.nodeType == 3pro textové uzly,elem.nodeType == 9pro objekt dokumentu,- ve specifikaci je uvedeno nÄkolik dalÅ¡Ãch hodnot.
PÅÃklad:
<body>
<script>
let elem = document.body;
// prozkoumejme: jaký druh uzlu je v elem?
alert(elem.nodeType); // 1 => element
// a jeho prvnà dÃtÄ je...
alert(elem.firstChild.nodeType); // 3 => text
// pro objekt document je typ 9
alert( document.nodeType ); // 9
</script>
</body>
V modernÃch skriptech můžeme ke zjiÅ¡tÄnà typu uzlu použÃt instanceof a dalšà testy založené na tÅÃdách, ale nodeType může být nÄkdy jednoduššÃ. Vlastnost nodeType můžeme jenom ÄÃst, ne mÄnit.
ZnaÄka: nodeName a tagName
Máme-li DOM uzel, můžeme naÄÃst název jeho znaÄky z vlastnostà nodeName nebo tagName:
PÅÃklad:
alert( document.body.nodeName ); // BODY
alert( document.body.tagName ); // BODY
Je nÄjaký rozdÃl mezi tagName a nodeName?
JistÄ, rozdÃl se odrážà v jejich názvech, ale to je samozÅejmÄ pÅÃliÅ¡ jemné.
- Vlastnost
tagNameexistuje pouze pro uzly tÅÃdyElement. - Vlastnost
nodeNameje definována pro každýNode:- u elementů má stejný význam jako
tagName. - u uzlů jiných typů (textové, komentáÅové atd.) obsahuje ÅetÄzec s typem uzlu.
- u elementů má stejný význam jako
Jinými slovy, tagName je podporována jen v elementových uzlech (protože pocházà ze tÅÃdy Element), zatÃmco nodeName nám může nÄco ÅÃci i o uzlech jiných typů.
Srovnejme napÅÃklad tagName a nodeName pro document a komentáÅový uzel:
<body><!-- komentáŠ-->
<script>
// pro komentáÅ
alert( document.body.firstChild.tagName ); // undefined (nenà to element)
alert( document.body.firstChild.nodeName ); // #comment
// pro dokument
alert( document.tagName ); // undefined (nenà to element)
alert( document.nodeName ); // #document
</script>
</body>
Jestliže pracujeme pouze s elementy, můžeme použÃvat tagName i nodeName â nenà v tom žádný rozdÃl.
ProhlÞeÄ má dva režimy zpracovánà dokumentů: HTML a XML. Pro webové stránky se obvykle použÃvá režim HTML. Režim XML je povolen, když prohlÞeÄ obdržà XML dokument s hlaviÄkou: Content-Type: application/xml+xhtml.
V režimu HTML je hodnota tagName/nodeName vždy velkými pÃsmeny: bude to BODY pro <body> i pro <BoDy>.
V režimu XML jsou velká a malá pÃsmena ponechána beze zmÄny. V souÄasnosti se režim XML použÃvá zÅÃdkakdy.
innerHTML: obsah
Vlastnost innerHTML umožÅuje zÃskat HTML kód uvnitÅ elementu jako ÅetÄzec.
Můžeme ji také mÄnit. Je to tedy jeden z nejsilnÄjÅ¡Ãch způsobů, jak mÄnit stránku.
NásledujÃcà pÅÃklad zobrazà obsah document.body a pak jej celý nahradà za jiný:
<body>
<p>Odstavec</p>
<div>Div</div>
<script>
alert( document.body.innerHTML ); // naÄte aktuálnà obsah
document.body.innerHTML = 'Nové TÄLO!'; // nahradà jej za jiný
</script>
</body>
Můžeme se pokusit vložit vadný HTML kód, prohlÞeÄ naÅ¡e chyby opravÃ:
<body>
<script>
document.body.innerHTML = '<b>test'; // zapomnÄli jsme uzavÅÃt znaÄku
alert( document.body.innerHTML ); // <b>test</b> (opraveno)
</script>
</body>
Jestliže innerHTML vložà do dokumentu znaÄku <script>, skript se stane souÄástà HTML kódu, ale nespustà se.
Pozor: âinnerHTML+=â obsah zcela pÅepÃÅ¡e
Můžeme pÅidat HTML kód do elementu pomocà elem.innerHTML+="dalšà html".
TÅeba takto:
chatDiv.innerHTML += "<div>Ahoj<img src='smile.gif'/> !</div>";
chatDiv.innerHTML += "Jak se máš?";
PÅi tom bychom si vÅ¡ak mÄli dávat velký pozor, protože to, co se stane, nebude pÅidánà obsahu, ale jeho úplné pÅepsánÃ.
Technicky následujÃcà dva Åádky udÄlajà totéž:
elem.innerHTML += "...";
// je kratšà způsob, jak napsat:
elem.innerHTML = elem.innerHTML + "..."
Jinými slovy, innerHTML+= udÄlá následujÃcÃ:
- Odstranà starý obsah.
- ZapÃÅ¡e mÃsto nÄj nový obsah
innerHTML(zÅetÄzenà starého a nového obsahu).
Když je obsah âvynulovánâ a od základů pÅepsán, vÅ¡echny obrázky a dalšà zdroje budou znovu naÄteny.
V uvedeném pÅÃkladu chatDiv Åádek chatDiv.innerHTML+="Jak se máš?" znovu vytvoÅà HTML obsah a znovu naÄte smile.gif (doufejme, že je v mezipamÄti). Jestliže chatDiv obsahuje spoustu dalÅ¡Ãho textu a obrázků, bude opakované naÄtenà jasnÄ viditelné.
Má to i dalšà vedlejšà efekty. NapÅÃklad jestliže návÅ¡tÄvnÃk vybral myšà existujÃcà text, vÄtÅ¡ina prohlÞeÄů po pÅepsánà innerHTML výbÄr zruÅ¡Ã. A pokud v nÄm byl <input>, do nÄhož návÅ¡tÄvnÃk zadal text, bude tento text odstranÄn. A podobnÄ.
NaÅ¡tÄstà kromÄ innerHTML existujà i jiné způsoby, jak pÅidat HTML kód. Brzy je prostudujeme.
outerHTML: celý HTML kód elementu
Vlastnost outerHTML obsahuje celý HTML kód elementu. Je to jako innerHTML plus element samotný.
Zde je pÅÃklad:
<div id="elem">Ahoj <b>svÄte</b></div>
<script>
alert(elem.outerHTML); // <div id="elem">Ahoj <b>svÄte</b></div>
</script>
Pozor: na rozdÃl od innerHTML zápis do outerHTML nezmÄnà element, ale mÃsto toho jej v DOMu nahradà novým.
Ano, znà to zvláštnÄ a zvláštnà to také je, proto tomu zde vÄnujeme zvláštnà poznámku. PodÃvejte se.
Uvažujme tento pÅÃklad:
<div>Ahoj, svÄte!</div>
<script>
let div = document.querySelector('div');
// nahradà div.outerHTML za <p>...</p>
div.outerHTML = '<p>Nový element</p>'; // (*)
// Páni! 'div' je poÅád stejný!
alert(div.outerHTML); // <div>Ahoj, svÄte!</div> (**)
</script>
To vypadá opravdu divnÄ, že?
Na Åádku (*) jsme nahradili div za <p>Nový element</p>. Ve vnÄjÅ¡Ãm dokumentu (DOMu) vidÃme mÃsto <div> nový obsah. Ale jak vidÃme na Åádku (**), hodnota staré promÄnné div se nezmÄnila!
PÅiÅazenà do outerHTML nezmÄnà DOM element (objekt, na který v tomto pÅÃpadÄ odkazuje promÄnná div), ale odstranà jej z DOMu a na jeho mÃsto vložà nový HTML kód.
PÅi div.outerHTML=... se tedy stalo následujÃcÃ:
divbyl odstranÄn z dokumentu.- Na jeho mÃsto byl vložen jiný kousek HTML kódu
<p>Nový element</p>. divmá stále svou původnà hodnotu. Nový HTML kód se neuložil do žádné promÄnné.
Je velmi snadné udÄlat tady chybu: zmÄnit div.outerHTML a pak pokraÄovat v práci s div, jako by v sobÄ mÄl nový obsah. Ale tak to nenÃ. NÄco takového je v poÅádku pro innerHTML, ale ne pro outerHTML.
Můžeme do elem.outerHTML zapisovat, ale mÄli bychom mÃt na pamÄti, že tÃm nezmÄnÃme element, do nÄhož zapisujeme (elem). NamÃsto toho vložÃme na jeho mÃsto nový HTML kód. Odkazy na nové elementy můžeme zÃskat prohledávánÃm DOMu.
nodeValue/data: obsah textového uzlu
Vlastnost innerHTML platà jen pro elementové uzly.
Jiné druhy uzlů, napÅÃklad textové, majà jejà protÄjÅ¡ek: vlastnosti nodeValue a data. Pro praktické použità jsou obÄ témÄÅ stejné, jsou mezi nimi jen drobné rozdÃly ve specifikaci. Budeme tedy použÃvat data, protože je kratÅ¡Ã.
PÅÃklad naÄtenà obsahu textového uzlu a komentáÅe:
<body>
Ahoj
<!-- KomentáŠ-->
<script>
let text = document.body.firstChild;
alert(text.data); // Ahoj
let komentáŠ= text.nextSibling;
alert(komentáÅ.data); // KomentáÅ
</script>
</body>
U textových uzlů si dokážeme pÅedstavit důvod, proÄ je naÄÃtáme nebo mÄnÃme, ale u komentáÅů?
VývojáÅi do nich nÄkdy vkládajà informace nebo instrukce pro Å¡ablony v HTML, napÅÃklad:
<!-- if jeSprávce -->
<div>VÃtáme vás, pane správce!</div>
<!-- /if -->
â¦Pak JavaScript může ÄÃst z jejich vlastnosti data a zpracovat vložené instrukce.
textContent: Äistý text
Vlastnost textContent poskytuje pÅÃstup k textu uvnitÅ elementu: obsahuje pouze text, zbavený vÅ¡ech <znaÄek>.
PÅÃklad:
<div id="zprávy">
<h1>Titulek!</h1>
<p>MarÅ¥ané útoÄà na lidi!</p>
</div>
<script>
// Titulek! MarÅ¥ané útoÄà na lidi!
alert(zprávy.textContent);
</script>
Jak vidÃme, vrátila nám pouhý text. VÅ¡echny jeho <znaÄky> byly odstranÄny, ale text z nich zůstal.
V praxi je naÄÃtánà takového textu zapotÅebà jen zÅÃdka.
Mnohem užiteÄnÄjšà je do textContent zapisovat, protože nám umožÅuje âbezpeÄnýâ zápis textu.
ÅeknÄme, že máme libovolný ÅetÄzec, napÅÃklad zadaný uživatelem, a chceme jej zobrazit.
- PomocÃ
innerHTMLjej vložÃme âjako HTML kódâ se vÅ¡emi HTML znaÄkami. - PomocÃ
textContentjej vložÃme âjako textâ, se vÅ¡emi symboly se zacházà doslovnÄ.
Porovnejte si tyto dva způsoby:
<div id="elem1"></div>
<div id="elem2"></div>
<script>
let jméno = prompt("Jak se jmenujeÅ¡?", "<b>MedvÃdek Pú!</b>");
elem1.innerHTML = jméno;
elem2.textContent = jméno;
</script>
- PrvnÃ
<div>obdržà jméno âjako HTMLâ: ze vÅ¡ech znaÄek se stanou znaÄky, takže jméno vidÃme tuÄnÄ. - Druhý
<div>obdržà jméno âjako text", takže vidÃme doslova<b>MedvÃdek Pú!</b>.
Ve vÄtÅ¡inÄ pÅÃpadů oÄekáváme, že uživatel zadá text, a chceme s nÃm zacházet jako s textem. Nechceme mÃt na stránce neoÄekávaný HTML kód. PÅiÅazenÃm do textContent toho dosáhneme.
Vlastnost âhiddenâ
Atribut hidden a stejnojmenná DOM vlastnost specifikuje, zda je element viditelný nebo ne.
Můžeme jej použÃvat v HTML nebo do nÄj pÅiÅadit hodnotu v JavaScriptu, tÅeba takto:
<div>Oba následujÃcà divy jsou skryté</div>
<div hidden>Pomocà atributu âhiddenâ</div>
<div id="elem">JavaScript nastavil vlastnost âhiddenâ</div>
<script>
elem.hidden = true;
</script>
Technicky hidden funguje stejnÄ jako style="display:none", ale je kratšà na napsánÃ.
Zde je blikajÃcà element:
<div id="elem">BlikajÃcà element</div>
<script>
setInterval(() => elem.hidden = !elem.hidden, 1000);
</script>
Dalšà vlastnosti
DOM elementy majà i dalšà vlastnosti, zejména ty, které závisejà na tÅÃdÄ:
valueâ hodnota<input>,<select>a<textarea>(HTMLInputElement,HTMLSelectElementâ¦).hrefâ âhrefâ pro<a href="...">(HTMLAnchorElement).idâ hodnota atributu âidâ, pro vÅ¡echny elementy (HTMLElement).- â¦a mnoho dalÅ¡Ãchâ¦
PÅÃklad:
<input type="text" id="elem" value="hodnota">
<script>
alert(elem.type); // "text"
alert(elem.id); // "elem"
alert(elem.value); // hodnota
</script>
VÄtÅ¡ina standardnÃch HTML atributů má odpovÃdajÃcà DOM vlastnost a my k nà můžeme takto pÅistupovat.
Pokud chcete znát celý seznam podporovaných vlastnostà v urÄité tÅÃdÄ, můžete jej najÃt ve specifikaci. NapÅÃklad HTMLInputElement je zdokumentován v https://html.spec.whatwg.org/#htmlinputelement.
Nebo pokud je chceme rychle zjistit nebo nás zajÃmá specifikace pro konkrétnà prohlÞeÄ, můžeme si prvek vždy vypsat pomocà console.dir(elem) a vlastnosti si pÅeÄÃst. Nebo můžeme prozkoumat âVlastnosti DOMuâ (DOM properties) v záložce Elements v prohlÞeÄových vývojáÅských nástrojÃch.
ShrnutÃ
Každý DOM uzel patÅà do urÄité tÅÃdy. Tyto tÅÃdy tvoÅà hierarchii. Výsledkem dÄdiÄnosti je celá sada vlastnostà a metod.
Hlavnà vlastnosti DOM uzlů jsou:
nodeType- Můžeme ji použÃt ke zjiÅ¡tÄnÃ, zda uzel je textový nebo elementový. Vlastnost má ÄÃselnou hodnotu:
1pro elementy,3pro textové uzly, nÄkolik dalÅ¡Ãch pro jiné typy uzlů. Pouze pro ÄtenÃ. nodeName/tagName- U elementů název znaÄky (velkými pÃsmeny, nejsme-li v režimu XML). U jiných uzlů
nodeNamepopisuje, co je to za uzel. Pouze pro ÄtenÃ. innerHTML- HTML obsah elementu. Může být mÄnÄn.
outerHTML- Ãplný HTML kód elementu. Operace zápisu do
elem.outerHTMLse nedotkne samotnéhoelem, ale mÃsto toho jej ve vnÄjÅ¡Ãm kontextu nahradà novým HTML kódem. nodeValue/data- Obsah neelementového uzlu (textového, komentáÅového). ObÄ vlastnosti jsou témÄÅ stejné, obvykle použÃváme
data. Mohou být mÄnÄny. textContent- Text uvnitÅ elementu: HTML zbavený vÅ¡ech
<znaÄek>. Zápis do této vlastnosti vložà text do elementu tak, že se vÅ¡emi speciálnÃmi znaky a znaÄkami se bude zacházet jako s textem. Dokáže bezpeÄnÄ vložit uživatelem vytvoÅený text a ochránit jej pÅed nechtÄnými zmÄnami v HTML. hidden- Když se nastavà na
true, udÄlá totéž jakodisplay:nonev CSS.
DOM uzly obsahujà i jiné vlastnosti, které závisejà na jejich tÅÃdÄ. NapÅÃklad elementy <input> (HTMLInputElement) podporujà value, type, zatÃmco elementy <a> (HTMLAnchorElement) podporujà href a podobnÄ. VÄtÅ¡ina standardnÃch HTML atributů má odpovÃdajÃcà DOM vlastnost.
NicménÄ HTML atributy a DOM vlastnosti nejsou vždy totožné, jak uvidÃme v pÅÃÅ¡tà kapitole.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)