// htmltahak/ · 09 z 09 — poslední stránka

Skripty & Interaktivita

script, noscript, dialog, details, summary, template + kompletní přehled globálních atributů — data-*, aria-*, tabindex a další.

<script>

JavaScript

Vloží nebo načte JS. defer/async zabrání blokování parsování. Vždy preferujte defer v head.

BLOCKHTML5

Script bez atributů blokuje parsování HTML — prohlížeč zastaví čtení stránky, stáhne a spustí JS, pak pokračuje. defer = spustí po parsování, zachová pořadí skriptů. async = spustí hned po stažení bez čekání. type="module" má automaticky defer.

// defer vs async — kdy codefer: zachová pořadí, spustí těsně před DOMContentLoaded. Ideální pro aplikační skripty závislé na sobě. async: spustí kdykoli po stažení — náhodné pořadí. Vhodné pro nezávislou analytiku nebo reklamy. Nikdy oba najednou.
Atributy
srcURL externího JS souboruURL
deferSpustí po parsování HTML, zachová pořadíboolean
asyncSpustí hned po stažení, bez pořadíboolean
typemodule = ES modules (auto defer), importmapmodule
nomoduleFallback pro prohlížeče bez ES modulesboolean
integritySRI hash pro bezpečnost CDN skriptůsha384-...
crossoriginCORS pro integrity checkanonymous
  • defer zachovává pořadí, async ne
  • type="module" je automaticky defer
  • Nikdy defer a async zároveň
  • Inline JS: vyhněte se v produkci (CSP bezpečnost)
  • Script na konci body = starý způsob, defer v head je lepší
Příklady
HTML
<!-- ✅ DOPORUČENO: defer v head -->
<head>
  <script src="app.js" defer></script>
</head>

<!-- ✅ ES Modules (auto defer) -->
<script type="module" src="main.js">
</script>

<!-- ✅ Analytika: async -->
<script async
  src="https://analytics.example.com/tag.js">
</script>

<!-- ✅ SRI pro CDN bezpečnost -->
<script
  src="https://cdn.example.com/lib.js"
  integrity="sha384-abc123..."
  crossorigin="anonymous"
  defer
></script>

<!-- ✅ Import map (ES modules aliasy) -->
<script type="importmap">
{
  "imports": {
    "lodash": "/node_modules/lodash-es/lodash.js"
  }
}
</script>

<!-- Nomodule fallback -->
<script type="module" src="modern.js">
</script>
<script nomodule src="legacy.js">
</script>

<!-- ❌ Blokuje parsování -->
<script src="app.js"></script>
<script src="app.js" defer> v head
<script src="app.js"> bez atributu
<noscript>

Fallback bez JavaScriptu

Zobrazí obsah když JS není dostupný nebo zakázaný.

BLOCK

noscript zobrazí obsah pokud je JS vypnutý nebo nepodporovaný. V <head> může obsahovat link, style, meta. V <body> může obsahovat jakýkoliv HTML. Méně důležité dnes — ale stále relevantní pro SPA aplikace.

// Kdy noscript použítSPA aplikace (React, Vue) — celá stránka závisí na JS. Přidejte noscript varování. Google crawl občas ignoruje JS — noscript obsah se crawluje vždy. Dobré místo pro <meta name="robots"> redirect bez JS.
  • V head: jen meta, link, style elementy
  • V body: jakýkoliv HTML
  • Varování pro JS-závislé aplikace
  • Google vždy crawluje noscript obsah
Příklady
HTML
<!-- V head (jen meta/link/style) -->
<head>
  <noscript>
    <link rel="stylesheet"
      href="no-js-styles.css">
  </noscript>
</head>

<!-- V body -->
<body>
  <noscript>
    <div class="warning">
      <strong>JavaScript je nutný.</strong>
      <p>
        Pro správné fungování aplikace
        zapněte JavaScript v prohlížeči.
      </p>
    </div>
  </noscript>

  <div id="app"></div>
</body>
<dialog>

Nativní modal / dialog

Moderní HTML modal bez JS knihoven. showModal() = modal s focus trap. Escape zavře automaticky.

BLOCKHTML5MODERNÍ

dialog je nativní modální nebo nemodální dialog. showModal() = modal s automatickým focus trap (Tab uvízne uvnitř), Escape zavře automaticky, ::backdrop pro overlay. show() = nemodální dialog (bez overlay). Nahrazuje vlastní div.modal implementace.

// Proč nativní dialog místo div.modaldiv.modal: manuální focus trap (složitý JS), manuální Escape handler, ARIA atributy ručně, backdrop ručně. Nativní dialog: focus trap zadarmo, Escape zadarmo, ::backdrop zadarmo, správná ARIA sémantika automaticky. Méně kódu, lepší přístupnost.
Atributy
openZobrazí dialog bez JS (bez overlaye!)boolean
JS metody
showModal()Otevře jako MODAL — s focus trap a overlay
show()Otevře jako nemodální dialog — bez overlay
close(value)Zavře dialog, volitelná návratová hodnota
returnValueHodnota předaná do close()
Živá ukázka
  • showModal() = modal s focus trap + Escape + overlay
  • show() = nemodální, bez overlay
  • ::backdrop pro stylování overlay
  • close() zavře, returnValue = výsledek
  • Klik na backdrop = klik na dialog element
Kompletní příklad
HTML + JS + CSS
<!-- Trigger -->
<button type="button"
  onclick="
    document.getElementById('dlg')
      .showModal()
  ">
  Smazat položku
</button>

<!-- Dialog -->
<dialog id="dlg">
  <h2>Potvrdit smazání</h2>
  <p>Opravdu smazat tuto položku?</p>
  <div class="dialog-btns">
    <button type="button"
      id="dlg-cancel">Zrušit</button>
    <button type="button"
      id="dlg-ok">Smazat</button>
  </div>
</dialog>

<script>
const dlg = document.getElementById('dlg');

// Zrušit
document.getElementById('dlg-cancel')
  .addEventListener('click', () => {
    dlg.close('cancel');
  });

// Potvrdit
document.getElementById('dlg-ok')
  .addEventListener('click', () => {
    deleteItem();
    dlg.close('ok');
  });

// Klik mimo dialog = zavřít
dlg.addEventListener('click', (e) => {
  if (e.target === dlg) dlg.close();
});

// Reakce na zavření
dlg.addEventListener('close', () => {
  console.log(dlg.returnValue); // ok/cancel
});
</script>

<style>
dialog {
  border: 1px solid #333;
  border-radius: 8px;
  padding: 1.5rem;
  max-width: 400px;
}
dialog::backdrop {
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
}
/* Animace otevření */
@keyframes slideIn {
  from { opacity: 0; transform: translateY(-20px); }
  to   { opacity: 1; transform: translateY(0); }
}
dialog[open] {
  animation: slideIn 0.2s ease-out;
}
</style>
<details> & <summary>

Accordion bez JavaScriptu

Nativní rozklikávací sekce. summary = klikatelný titulek. open = rozbaleno defaultně. Žádný JS!

BLOCKHTML5

details je nativní rozklikávací sekce bez JavaScriptu. summary je klikatelný titulek — musí být první child. Atribut open = rozbaleno defaultně. Klávesnice funguje nativně. Skvělé pro FAQ, spoilers, collapse sekce.

// name atribut — exclusive accordionHTML 2024: atribut name na details — jen jedno details se stejným name může být otevřeno najednou (exclusive accordion). Bez name: každé details je nezávislé. S name: zavírají se navzájem.
Atributy details
openRozbaleno defaultněboolean
nameExclusive accordion skupina (HTML 2024)string
Živá ukázka — FAQ accordion
Jak dlouho trvá doručení?
Standardně 2–3 pracovní dny. Express doručení do 24 hodin za příplatek 99 Kč.
Mohu vrátit zboží?
Ano, do 14 dnů od doručení bez udání důvodu. Zboží musí být nepoškozené a v originálním obalu.
Jak sledovat objednávku?
Po odeslání dostanete email s trackovacím číslem. Zásilku sledujte na webu dopravce.
  • summary musí být první child details
  • CSS ::marker pro vlastní šipku
  • name = exclusive accordion (HTML 2024)
  • Animace výšky: CSS nebo JS scrollHeight
  • Přístupné: klávesnice, Enter/Space, screen reader
Příklady
HTML + CSS
<!-- Základní FAQ -->
<details>
  <summary>Jak dlouho trvá doručení?</summary>
  <p>Standardně 2–3 pracovní dny.</p>
</details>

<!-- Defaultně otevřeno -->
<details open>
  <summary>Popis produktu</summary>
  <p>Victorinox Classic je...</p>
</details>

<!-- Exclusive accordion (HTML 2024) -->
<details name="faq">
  <summary>Otázka 1</summary>
  <p>Odpověď 1...</p>
</details>
<details name="faq">
  <summary>Otázka 2</summary>
  <p>Odpověď 2...</p>
</details>
<!-- Otevřením jednoho se ostatní zavřou -->

<!-- CSS -->
<style>
details {
  border: 1px solid #eee;
  border-radius: 6px;
  margin-bottom: 0.5rem;
}
summary {
  padding: 0.75rem 1rem;
  cursor: pointer;
  font-weight: 600;
  list-style: none; /* skryje default šipku */
}
/* Vlastní šipka */
summary::before {
  content: '▸ ';
  transition: transform 0.2s;
  display: inline-block;
}
details[open] summary::before {
  transform: rotate(90deg);
}
details > :not(summary) {
  padding: 0 1rem 0.75rem;
}
/* Animace (CSS-only) */
details[open] > :not(summary) {
  animation: fadeDown 0.2s ease;
}
@keyframes fadeDown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
</style>
<template>

HTML šablona

Kus HTML který se nerendiruje ani nespouští. Klonuje se JavaScriptem pro dynamické přidávání obsahu.

BLOCKHTML5

template obsahuje HTML které není renderováno ani stahováno (obrázky, skripty uvnitř se nespustí). Klonujeme ho JS pomocí content.cloneNode(true) pro dynamické vytváření elementů. Bezpečnější alternativa k innerHTML — žádné XSS riziko.

// template vs innerHTMLinnerHTML: riziko XSS (uživatelský obsah jako HTML), pomalé (celé DOM přeparsování). template.content.cloneNode(): bezpečné, rychlé, typově správné DOM operace. Web Components template hodně využívají.
Atributy
idIdentifikátor pro JS selekcistring
shadowrootmodeDeclarative Shadow DOM (nový)open/closed
Alternativy
innerHTML (méně bezpečné) createElement + appendChild JSX / React (v JS frameworku)
  • content.cloneNode(true) = hluboká kopie
  • Skripty a obrázky uvnitř se nespustí
  • Bezpečnější než innerHTML
  • Web Components: základ pro custom elementy
Příklad — dynamické karty produktů
HTML + JS
<!-- Šablona (neviditelná) -->
<template id="product-card">
  <div class="card">
    <img class="card-img" alt="">
    <div class="card-body">
      <h3 class="card-title"></h3>
      <p class="card-price"></p>
      <button type="button"
        class="card-btn">
        Do košíku
      </button>
    </div>
  </div>
</template>

<!-- Kontejner -->
<div id="product-list"></div>

<script>
const products = [
  {
    id: 1,
    nazev: 'Victorinox Classic',
    cena: '299 Kč',
    img: 'classic.jpg'
  },
  {
    id: 2,
    nazev: 'Gerber Paraframe',
    cena: '549 Kč',
    img: 'paraframe.jpg'
  }
];

const tmpl = document.getElementById(
  'product-card'
);
const list = document.getElementById(
  'product-list'
);

products.forEach(p => {
  // Klonovat šablonu
  const clone = tmpl.content.cloneNode(true);

  // Vyplnit data
  clone.querySelector('.card-img').src = p.img;
  clone.querySelector('.card-img').alt = p.nazev;
  clone.querySelector('.card-title')
    .textContent = p.nazev;
  clone.querySelector('.card-price')
    .textContent = p.cena;
  clone.querySelector('.card-btn')
    .dataset.productId = p.id;

  // Přidat do DOM
  list.appendChild(clone);
});
</script>
Globální atributy

Atributy platné na každém elementu

id, class, data-*, aria-*, lang, hidden, tabindex, contenteditable, draggable...

VŠECHNY ELEMENTY

Globální atributy lze použít na každém HTML elementu. Nejdůležitější skupiny: identifikace (id, class), datové atributy (data-*), přístupnost (aria-*, role, tabindex), chování (hidden, contenteditable, draggable).

// data-* atributy — kdy použítdata-* jsou bezpečný způsob ukládání vlastních dat přímo v HTML pro JS. Čteme přes el.dataset.nazevAtributu (camelCase). Alternativa: JS Map/WeakMap mimo DOM. data-* je vhodné pro propojení HTML a JS bez vlastních atributů.
Přehled globálních atributů
idUnikátní identifikátor na stránce. Pro CSS #id a JS getElementById
classCSS třídy oddělené mezerou. Pro CSS .class a JS classList
styleInline CSS styly. Vyhýbejte se — raději class
titleTooltip text při najetí myší. Pro abbr, img, iframe
langJazyk obsahu elementu (cs, en, de). Pro screen reader
dirSměr textu: ltr, rtl, auto. Pro arabštinu/hebrejštinu
hiddenSkryje element (display: none). JS: el.hidden = true
tabindex0 = fokusovatelné v přirozeném pořadí. -1 = jen JS focus. 1+ = pořadí (nepoužívat!)
contenteditabletrue/false — uživatel může editovat obsah elementu
draggabletrue/false — přetažitelný prvek (Drag & Drop API)
spellchecktrue/false — kontrola pravopisu prohlížečem
translateyes/no — přeložit? no pro brandové názvy
inertElement je "neaktivní" — nefokusovatelný, neklikalný
data-*Vlastní datový atribut. data-user-id="42" → el.dataset.userId
aria-labelPřístupný popis pro screen readery (když není viditelný text)
aria-labelledbyID elementu který popisuje tento element
aria-describedbyID elementu s podrobným popisem
aria-hidden"true" = skryje element před screen readery. Pro dekorativní prvky
aria-expandedStav rozbalení: true/false. Pro menu, accordion, dropdown
aria-liveDynamické oblasti: polite/assertive. Oznamuje změny obsahu
aria-controlsID elementu který tento element ovládá
roleSémantická role: button, dialog, navigation, alert, status...
Příklady data-* a aria-*
HTML + JS
<!-- data-* atributy -->
<button
  type="button"
  data-product-id="42"
  data-price="299"
  data-category="nože"
  onclick="addToCart(this)"
>
  Do košíku
</button>

<script>
function addToCart(btn) {
  // el.dataset.atributName (camelCase)
  const id  = btn.dataset.productId; // "42"
  const price = btn.dataset.price;   // "299"
  const cat = btn.dataset.category;  // "nože"
  console.log(id, price, cat);
}
</script>

<!-- aria-* pro přístupnost -->
<button
  type="button"
  aria-expanded="false"
  aria-controls="menu"
  id="menu-btn"
>
  Menu
</button>

<ul id="menu" hidden
  role="menu"
  aria-labelledby="menu-btn">
  <li role="menuitem">
    <a href="/">Domů</a>
  </li>
</ul>

<script>
const btn  = document.getElementById('menu-btn');
const menu = document.getElementById('menu');

btn.addEventListener('click', () => {
  const isOpen = btn.getAttribute(
    'aria-expanded'
  ) === 'true';
  btn.setAttribute('aria-expanded', !isOpen);
  menu.hidden = isOpen;
});
</script>

<!-- tabindex -->
<!-- 0 = fokus v přirozeném pořadí -->
<div tabindex="0" role="button"
  onkeydown="handleKey(event)"
  onclick="doSomething()">
  Klikatelný div (raději button!)
</div>

<!-- -1 = fokus jen z JS -->
<div id="modal-content" tabindex="-1">
  Modal obsah
</div>
<script>
// Při otevření modalu
document.getElementById('modal-content')
  .focus();
</script>

<!-- aria-live region -->
<div aria-live="polite"
  aria-atomic="true"
  id="cart-status">
  3 položky v košíku
</div>
<script>
// Jakákoli změna obsahu bude oznámena
document.getElementById('cart-status')
  .textContent = '4 položky v košíku';
// Screen reader automaticky přečte změnu
</script>

<!-- contenteditable -->
<h1 contenteditable="true"
  spellcheck="true"
  data-placeholder="Klikněte a editujte">
  Nadpis ke editaci
</h1>
🎉

HTML Taháček dokončen!

Prošli jste všech 9 stránek — přes 80 HTML tagů s vysvětlením, příklady a tipy.

Pokračovat na CSS Taháček →
← 08 Tabulky 09 / 09 — Skripty & Interaktivita ↑ Přehled

Nativní HTML dialog

Toto je ukázka <dialog> elementu. Focus trap funguje automaticky — Tab uvázne uvnitř. Escape zavře dialog.