form, input (všechny typy!), label, select, textarea, button, fieldset, legend — kompletní průvodce HTML formuláři s důrazem na přístupnost a UX.
Kontejner pro formulářové prvky. action = URL odesílání, method = GET/POST.
form obaluje formulářové prvky a definuje jak se data odešlou. action = URL pro odeslání. method = HTTP metoda. GET = data v URL (vyhledávání, sdílitelné). POST = data v těle requestu (citlivá data, upload).
| action | URL pro odeslání formulářových dat | URL |
| method | GET = URL params (vyhledávání), POST = tělo | GET / POST |
| enctype | Pro file upload: multipart/form-data | multipart/form-data |
| novalidate | Vypne HTML5 validaci (pro vlastní JS) | boolean |
| autocomplete | Zapnout/vypnout autocomplete prohlížeče | on / off |
<!-- Klasický POST formulář -->
<form action="/kontakt" method="POST">
<label for="jmeno">Jméno:</label>
<input type="text" id="jmeno"
name="jmeno" required>
<button type="submit">Odeslat</button>
</form>
<!-- AJAX odeslání -->
<form id="myForm" novalidate>
<!-- pole... -->
<button type="submit">Odeslat</button>
</form>
<script>
document.getElementById('myForm')
.addEventListener('submit', async (e) => {
e.preventDefault();
const data = new FormData(e.target);
try {
const res = await fetch('/api/kontakt', {
method: 'POST',
body: data
});
if (res.ok) alert('Odesláno!');
} catch (err) {
console.error(err);
}
});
</script>
<!-- Upload souboru -->
<form method="POST"
enctype="multipart/form-data">
<input type="file" name="foto">
<button type="submit">Nahrát</button>
</form>
Nejpoužívanější formulářový prvek. Void element. Atribut type mění vše — validaci, klávesnici, UX.
Správný type = lepší mobilní klávesnice + vestavěná validace + správné UX. Například: type="email" → na mobilu klávesnice s @. type="tel" → numerická klávesnice. type="date" → nativní datový picker.
| type | K čemu | Speciální chování |
|---|---|---|
| text | Libovolný text (výchozí) | — |
| Emailová adresa | Validuje formát, @ klávesnice | |
| password | Heslo | Skryje znaky tečkami |
| number | Číslo | Šipky nahoru/dolů, min/max/step |
| tel | Telefonní číslo | Numerická klávesnice na mobilu |
| url | Webová adresa | Validuje formát URL |
| search | Vyhledávací pole | Křížek pro smazání |
| date | Datum | Nativní datový picker |
| time | Čas | Nativní výběr času |
| datetime-local | Datum + čas | Kombinovaný picker |
| month | Měsíc + rok | Picker měsíce |
| week | Týden v roce | Picker týdne |
| checkbox | Zaškrtávací políčko | checked atribut, boolean |
| radio | Přepínač (výběr jednoho) | Stejné name = skupina |
| range | Posuvník | min/max/step, value = výchozí |
| color | Výběr barvy | Color picker prohlížeče |
| file | Upload souboru/ů | accept, multiple |
| hidden | Skryté pole | Neviditelné, odesílá se s formulářem |
| submit | Odesílací tlačítko | Raději <button type="submit"> |
| reset | Reset formuláře | Vymaže všechna pole |
| button | Tlačítko bez akce | Raději <button type="button"> |
| image | Obrázkové submit tlačítko | src + alt povinné |
| type | Typ pole — viz tabulka vlevo | string |
| name | Klíč v odesílaných datech. Povinné! | string |
| id | Propojení s label[for]. Musí být unikátní. | string |
| value | Výchozí hodnota nebo hodnota pro submit | string |
| placeholder | Nápověda (mizí při psaní — nenahrazuje label!) | string |
| required | Povinné pole — neodesle se bez hodnoty | boolean |
| disabled | Zakáže pole (neodesílá se!) | boolean |
| readonly | Jen pro čtení (ale odesílá se) | boolean |
| min / max | Rozsah pro number, date, range | číslo / datum |
| step | Krok pro number, range | číslo |
| pattern | Regex pro validaci textu | regex |
| minlength / maxlength | Min/max délka textu | číslo |
| autocomplete | Napovídání prohlížeče | on/off/email/name |
| accept | Pro file: povolené typy souborů | .jpg,.pdf,image/* |
| multiple | Více hodnot (file, email) | boolean |
| checked | Výchozí stav checkbox/radio | boolean |
<!-- Text -->
<input type="text" name="jmeno"
placeholder="Vaše jméno" required>
<!-- Email -->
<input type="email" name="email"
autocomplete="email">
<!-- Heslo s min délkou -->
<input type="password" name="heslo"
minlength="8" required>
<!-- Číslo s rozsahem -->
<input type="number" name="mnozstvi"
min="1" max="99" value="1">
<!-- Datum -->
<input type="date" name="datum"
min="2024-01-01">
<!-- Checkbox -->
<input type="checkbox" id="souhlas"
name="souhlas" required>
<label for="souhlas">
Souhlasím s podmínkami
</label>
<!-- Radio skupina -->
<input type="radio" id="muz"
name="gender" value="muz">
<label for="muz">Muž</label>
<input type="radio" id="zena"
name="gender" value="zena">
<label for="zena">Žena</label>
<!-- Upload (jen obrázky) -->
<input type="file" name="foto"
accept="image/*" multiple>
<!-- Slider -->
<input type="range" name="hlasitost"
min="0" max="100" value="50">
<!-- Barva -->
<input type="color" name="barva"
value="#F75B9E">
<!-- Skryté pole (CSRF token) -->
<input type="hidden" name="csrf"
value="abc123xyz">
<!-- Pattern validace (IČO 8 číslic) -->
<input type="text" name="ico"
pattern="[0-9]{8}"
title="IČO musí mít 8 číslic">
Propojí textový popis s formulářovým polem. Povinné pro přístupnost. Klik na label = focus na pole.
label propojí popis s formulářovým polem. Dvě metody: for="id-pole" nebo obalení pole labellem. Klik na label = focus/check na poli — větší klikatelná plocha. Bez label je formulář nepřístupný pro screen readery.
| for | Musí odpovídat id vstupního pole | id-pole |
<span aria-hidden="true">*</span><!-- Metoda 1: for + id -->
<label for="email">Email:</label>
<input type="email"
id="email" name="email">
<!-- Metoda 2: obalení (bez id) -->
<label>
Heslo:
<input type="password" name="heslo">
</label>
<!-- Checkbox — obalení = velká plocha -->
<label>
<input type="checkbox" name="novinky">
Přihlásit k odběru novinek
</label>
<!-- Povinné pole -->
<label for="email">
Email
<span aria-hidden="true"
style="color:red">*</span>
</label>
<input type="email" id="email"
required aria-required="true">
<!-- Skrytý label (visually hidden) -->
<label for="search" class="sr-only">
Hledat na webu
</label>
<input type="search" id="search"
placeholder="Hledat...">
<style>
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
white-space: nowrap;
border: 0;
}
</style>
Výběr z předdefinovaných možností. option = položka, optgroup = kategorie skupin.
select je rozbalovací seznam. Uvnitř jsou <option> elementy. Pro skupiny použijte <optgroup>. multiple umožní vybrat více položek (Ctrl+klik). Pro 2–4 možnosti zvažte radio buttons — lepší UX.
| name | Klíč v odesílaných datech | string |
| multiple | Vícenásobný výběr (Ctrl+klik) | boolean |
| size | Počet viditelných položek najednou | číslo |
| required | Povinný výběr | boolean |
| disabled | Zakáže select | boolean |
<!-- Základní select -->
<label for="kraj">Kraj:</label>
<select id="kraj" name="kraj" required>
<option value="">
-- Vyberte kraj --
</option>
<optgroup label="Čechy">
<option value="PH">Praha</option>
<option value="JC">Jihočeský</option>
<option value="JM" selected>
Jihomoravský
</option>
</optgroup>
<optgroup label="Morava">
<option value="MS">
Moravskoslezský
</option>
</optgroup>
</select>
<!-- Multiple výběr -->
<label for="jazyky">Jazyky:</label>
<select id="jazyky" name="jazyky[]"
multiple size="4">
<option value="cs">Čeština</option>
<option value="en">Angličtina</option>
<option value="de">Němčina</option>
<option value="sk">Slovenština</option>
</select>
<small>Ctrl+klik = více možností</small>
<!-- Vlastní stylování -->
<style>
select {
appearance: none;
background-image: url("data:image/svg+xml,...");
background-repeat: no-repeat;
background-position: right 0.75rem center;
padding-right: 2rem;
}
</style>
Víceřádkové textové pole. Výchozí hodnota je obsah tagu. CSS resize řídí změnu velikosti.
textarea je víceřádkové textové pole. Výchozí hodnota je obsah elementu (ne atribut value jako u input!). Uživatel může tahat za roh a měnit velikost — ovládejte CSS resize.
| name | Klíč v datech | string |
| rows | Počet viditelných řádků (fallback) | číslo |
| cols | Počet viditelných sloupců (fallback) | číslo |
| required | Povinné pole | boolean |
| maxlength | Max počet znaků | číslo |
| minlength | Min počet znaků | číslo |
| placeholder | Nápověda (mizí při psaní) | string |
| readonly | Jen pro čtení (ale odesílá se) | boolean |
<!-- Základní textarea -->
<label for="zprava">Zpráva:</label>
<textarea
id="zprava"
name="zprava"
rows="5"
required
maxlength="1000"
placeholder="Napište nám..."
></textarea>
<!-- Textarea s počítadlem znaků -->
<label for="popis">Popis:</label>
<textarea id="popis" name="popis"
maxlength="500"
oninput="updateCount(this)"
></textarea>
<small id="count">0 / 500</small>
<script>
function updateCount(el) {
document.getElementById('count')
.textContent =
`${el.value.length} / ${el.maxLength}`;
}
</script>
<!-- Auto-resize výšky -->
<textarea id="autosize"
oninput="this.style.height='auto';
this.style.height=this.scrollHeight+'px'"
style="overflow:hidden"
></textarea>
<!-- CSS -->
<style>
textarea {
width: 100%;
resize: vertical; /* nebo: none */
min-height: 100px;
font-family: inherit;
font-size: 1rem;
}
</style>
type="submit" odesílá formulář (výchozí!), type="button" pro JS akce, type="reset" resetuje.
button je klikatelný prvek. Vždy explicitně nastavte type! Výchozí je submit — button bez type uvnitř formuláře formulář odešle. button může obsahovat HTML (text + ikona). Preferujte button před <input type="button">.
| type | submit = odešle form (DEFAULT!), button = nic, reset = vymaže | submit/button/reset |
| disabled | Zakáže tlačítko (neodesílá se) | boolean |
| name / value | Pro identifikaci tlačítka při odeslání | string |
| form | ID formuláře mimo DOM stromem | form-id |
| formaction | Přepsat action formuláře pro toto tlačítko | URL |
| aria-label | Popis pro screen readery (ikonová tlačítka) | string |
| aria-pressed | Toggle tlačítko: true/false | true/false |
<!-- Submit -->
<button type="submit">Odeslat</button>
<!-- JS akce (type="button" nutné!) -->
<button type="button"
onclick="toggleMenu()">
Menu ☰
</button>
<!-- Ikonové tlačítko -->
<button type="button"
aria-label="Smazat položku">
<svg aria-hidden="true">
<use href="#icon-delete"></use>
</svg>
</button>
<!-- Loading stav -->
<button type="submit" id="submitBtn">
Odeslat
</button>
<script>
document.querySelector('form')
.addEventListener('submit', (e) => {
const btn = document.getElementById(
'submitBtn'
);
btn.disabled = true;
btn.textContent = 'Odesílám...';
});
</script>
<!-- Toggle tlačítko -->
<button
type="button"
aria-pressed="false"
id="darkBtn"
onclick="
const p = this.getAttribute('aria-pressed');
this.setAttribute('aria-pressed', p !== 'true');
"
>
Tmavý režim
</button>
<!-- Dvě submit tlačítka, různé akce -->
<button type="submit"
formaction="/ulozit">
Uložit
</button>
<button type="submit"
formaction="/publikovat">
Publikovat
</button>
fieldset = sémanticky seskupí related pole. legend = název skupiny. Povinné pro radio a checkbox skupiny.
fieldset vizuálně a sémanticky seskupí related formulářová pole. legend je název skupiny — screen reader přečte legend při focusu každého pole uvnitř. Povinné pro radio buttons a checkbox skupiny — bez fieldset+legend screen reader nezná kontext.
| disabled | Zakáže všechna pole uvnitř najednou | boolean |
| name | Název skupiny | string |
| form | ID formuláře (pro fieldset mimo form) | form-id |
<!-- Radio skupina -->
<fieldset>
<legend>Pohlaví</legend>
<label>
<input type="radio"
name="gender" value="muz">
Muž
</label>
<label>
<input type="radio"
name="gender" value="zena">
Žena
</label>
<label>
<input type="radio"
name="gender" value="jine">
Jiné
</label>
</fieldset>
<!-- Adresová sekce -->
<fieldset>
<legend>Doručovací adresa</legend>
<label for="ulice">Ulice:</label>
<input type="text" id="ulice"
name="ulice" autocomplete="street-address">
<label for="mesto">Město:</label>
<input type="text" id="mesto"
name="mesto" autocomplete="address-level2">
</fieldset>
<!-- Disabled fieldset -->
<fieldset disabled>
<legend>Fakturační údaje</legend>
<!-- všechna pole zakázána -->
<input type="text" name="firma">
</fieldset>
<!-- CSS: bez rámečku -->
<style>
fieldset {
border: none;
padding: 0;
margin: 0 0 1.5rem;
}
legend {
font-weight: 700;
margin-bottom: 0.75rem;
}
</style>