
Zanořování nezanořitelných HTML značek
Jak funguje zanořování značek jako <p> nebo <button> v HTML parseru versus DOM metodách.
Některé HTML značky nelze do sebe zanořovat. Typickým příkladem jsou odstavce <p> nebo tlačítka <button>. HTML parser takové pokusy automaticky opraví. Ale co se stane, když stejnou strukturu vytvoříme pomocí DOM metod v JavaScriptu?
Problém s vnořováním
Zkusme napsat následující HTML kód, kde jeden odstavec zanořujeme do druhého:
<p>Vnější odstavec
<p>Vnitřní odstavec</p>
</p>
HTML parser tento kód automaticky opraví a výsledný DOM bude vypadat následovně:
<p>Vnější odstavec</p>
<p>Vnitřní odstavec</p>
<p></p>
Parser při zjištění otevírací značky <p> uvnitř jiného <p> automaticky uzavře vnější odstavec. Poslední zavírací značka </p> pak vytvoří prázdný odstavec, protože nemá co uzavírat.
Vytvoření přes DOM metody
Co se ale stane, když stejnou strukturu vytvoříme pomocí JavaScriptu a DOM metod createElement a appendChild?
var vnejsi = document.createElement("p");
var vnitrni = document.createElement("p");
vnitrni.textContent = "Vnitřní odstavec";
vnejsi.textContent = "Vnější odstavec ";
vnejsi.appendChild(vnitrni);
document.body.appendChild(vnejsi);
V tomto případě žádná automatická oprava neproběhne! Výsledný DOM skutečně obsahuje <p> uvnitř jiného <p>, což je v HTML nevalidní struktura.

Proč to funguje jinak?
Rozdíl je v tom, kdy se HTML parsuje:
- HTML parser zpracovává textový řetězec a podle HTML specifikace musí nevalidní struktury opravit. Parser ví, že
<p>nemůže obsahovat další<p>, a automaticky první uzavře. - DOM metody pracují přímo s objektovým modelem dokumentu. Když voláte
appendChild, pouze říkáte prohlížeči „přidej tento element jako potomka". Parser se na to už nedívá, protože nepracujete s HTML řetězcem.
Další příklady nezanořitelných značek
Podobně jako <p> se chovají i další značky:
Tlačítko <button>
Tlačítko nesmí obsahovat další tlačítko:
<!-- HTML parser automaticky opraví -->
<button>Vnější
<button>Vnitřní</button>
</button>
Ale přes DOM metody to jde:
var vnejsi = document.createElement("button");
var vnitrni = document.createElement("button");
vnitrni.textContent = "Vnitřní";
vnejsi.textContent = "Vnější ";
vnejsi.appendChild(vnitrni); // Funguje!
Vypadá to vskutku zajímavě:

Odkaz <a>
Odkaz nesmí obsahovat další interaktivní element, jako je další odkaz nebo tlačítko:
<!-- Nevalidní, parser opraví -->
<a href="url1">
<a href="url2">Vnořený odkaz</a>
</a>
Formulář <form>
Formulář nesmí obsahovat další formulář:
<!-- Nevalidní -->
<form>
<form></form>
</form>
Praktické důsledky
Tento rozdíl je důležitý zejména pro JavaScriptové frameworky a knihovny, které vytvářejí DOM dynamicky.
innerHTML vs createElement
Při použití innerHTML se HTML řetězec parsuje stejně jako běžný HTML kód, takže se aplikují všechna omezení:
// HTML parser opraví strukturu
element.innerHTML = '<p>Vnější<p>Vnitřní</p></p>';
Zatímco při použití DOM metod se omezení neaplikují:
// Vytvoří nevalidní, ale funkční strukturu
var p1 = document.createElement('p');
var p2 = document.createElement('p');
p1.appendChild(p2);
Frameworky jako Svelte
Některé frameworky (např. Svelte) generují kód, který používá DOM metody. To znamená, že mohou vytvořit HTML struktury, které by v čistém HTML nešly napsat.
Toto chování může vést k neočekávaným výsledkům, pokud komponenta vytvoří strukturu, která je technicky nevalidní, ale v DOM funguje. Při použití innerHTML nebo server-side renderingu se taková struktura může chovat jinak.
HTML validace
I když DOM metody umožňují vytvořit nevalidní struktury, nedoporučuje se to dělat záměrně:
- Může to způsobit nekonsistentní chování při různých způsobech renderování (CSR – klientské vs. SSR – serverové)
- Validátory HTML budou hlásit chyby
- Může to ovlivnit přístupnost (screen readery mohou mít problémy)
- Budoucí verse prohlížečů mohou takové struktury opravovat
Stylování vnořených nezanořitelných elementů
Pokud se vám přes DOM metody podaří vytvořit vnořené nezanořitelné elementy, může se jejich stylování chovat neočekávaně.
CSS dědičnost a specifičnost
CSS pravidla se aplikují normálně, protože prohlížeč vidí validní DOM strukturu (i když je nevalidní podle HTML specifikace).
Takže tento nesmyslný selektor bude fungovat:
p p {
color: blue;
}
Stylování vnořených tlačítek
U tlačítek je situace ještě komplikovanější. Tlačítko má speciální výchozí styly a chování (kursor, hover stavy, focus). Vnořené tlačítko zdědí některé vlastnosti, ale může mít problémy s:
- Kliknutím – které tlačítko se má aktivovat?
- Focus stavem – může dojít k neočekávanému visuálnímu zvýraznění
- Z-indexem – vnořené tlačítko může překrývat vnější
Správné řešení
Namísto vnořování nezanořitelných značek použijte jiné elementy:
<!-- Místo vnořených odstavců -->
<div>
<p>První odstavec</p>
<p>Druhý odstavec</p>
</div>
<!-- Místo vnořených tlačítek -->
<div class="button-group">
<button>První tlačítko</button>
<button>Druhé tlačítko</button>
</div>
Teoreticky jde problém obejít použitím <span role="button" onclick=""> a CSS pro stylování, ale lepší řešení je většinou do sebe tlačítka nezanořovat.
Visuálně jde efektu tlačítka v tlačítku docílit absolutním posicováním.
Závěr
HTML parser a DOM metody se chovají odlišně při práci s nezanořitelnými značkami:
- HTML parser (včetně
innerHTML) automaticky opraví nevalidní struktury podle HTML specifikace - DOM metody (
createElement,appendChild) umožňují vytvořit i nevalidní struktury, protože neprochází parserem - Tento rozdíl může způsobit problémy při různých způsobech renderování (CSR vs. SSR)
- Nejlepší je dodržovat HTML specifikaci a nevytvářet nevalidní struktury záměrně
Odkazy jinam
- HTML Living Standard – Oficiální HTML specifikace
- W3C Markup Validation Service – Validátor HTML
- DOM (Document Object Model) – Jak funguje DOM
- innerHTML – Práce s HTML obsahem v JS
- HTML tlačítko <button> – Více o tlačítkách
Související články
Možnosti stylování <iframe>
Co lze a nelze u <iframe> ovlivnit pomocí CSS a jak na změnu textu nebo barev.
