Formulářové prvky mají ve všech prohlížečích předdefinovaný styl (podle OS) a je obtížné je změnit. Přitom požadavek na změnu vzhledu formulářů je jeden z nejčastějších při vývoji RIA (Rich Internet Application) nebo PWA (Progressive Web App).
Původní text: listopad 2017
Aktualizováno: březen 2022
File input
Prvek pro nahrání souboru je jeden z nejobtížněji stylovatelných. Přesněji řečeno ve většině prohlížečů se vůbec stylovat nedá, vyjma rozměrů a některých vlastností, které vzhled přímo neovlivňují a jen mění, jak a kde se ve stránce vykreslí.
Toho se dá ale využít společně s faktem, že label
(popisek) přiřazený prvky vždy vyvolá focus
propojeného prvku. Pokud tedy skryjete samotný formulářový prvek pro upload a následně k němu připojíte label
, budete ho moci dle libosti stylovat a zachováte možnost soubor vybrat a nahrát.
Jedinou nevýhodou je to, že formulářový prvek nemůžete úplně skrýt (display: none
), protože by přestal fungovat. Skrytí ale lze dosáhnout právě pomocí CSS vlastností, které prvek vizuálně skryjí před uživateli, ale zároveň zachovají jeho funkčnost.
<label for=upload>
<input type=file id=upload>
</label>
Je jedno, jestli input
vložíte do label
u nebo někam jinam. Tenhle zápis je ale z hlediska organizace HTML zpravidla nejlepší (při debuggování stačí najít label a víte, že input je v něm).
label[for=upload] {
//... libovolný styl, jak je potřeba
}
input[type=file] {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
Nejjednodušší metoda skrytí prvku by byla nastavení absolutního pozicování a posunutí někam hodně daleko pryč. Nevýhodou tohoto řešení je, že prohlížeč musí vytvořit dostatečně velkou stránku, aby posun pojmul, což ho zbytečně zatěžuje.
Další řešení je nastavit viditelnost nebo průhlednost, což také prvek skryje. Problém ale je, že některé prohlížeče mohou neviditelný nebo zcela průhledný prvek považovat za skrytý a vypnout ho.
Nejlepší řešení je tedy využít vlastnost clip: rect()
, která prohlížeči říká, která část prvku se má vykreslit. Pokud zadáte samé nuly, znamená to, že prvek se nezobrazí vůbec. Vlastnost clip
ale funguje tak, že prvek nejprve celý vykreslí (takže bude fungovat), a pak teprve vybere, kterou část vykreslí (a když řekneme, že žádnou, prostě ho nechá skrytý, ale funkční.
Vlastnost clip
podporují prakticky všechny prohlížeče novější než IE7 a Opera 6 (takže v roce 2022+ to lze považovat za standard).
Select
Select (seznam) můžete celkem dobře stylovat ve většině prohlížečů s drobnými rozdíly a nedostatky.
U selectu vždy používejte přímo daný prvek místo toho, abyste ho nahrazovali nějakým vlastním inputem s rozbalovacím menu. U klasického selectu totiž dokáží mobilní zařízení zobrazit vlastní implementaci, která zabrání např. vytečení seznamu z obrazu a usnadňují výběr pomocí dotykového ovládání.
Pokud chcete stylovat položky v rozbaleném seznamu, musíte některé styly nastavit celému select
u a některé jen jeho option
.
Pokud chcete vycentrovat text select
u, nefunguje na to vlastnost text-align
, ale text-align-last
, která je podporována od IE 5.5 a všech ostatních prohlížečích kromě Safari.
Pro další odsazení textu pak můžete normálně použít padding
, ale je potřeba pamatovat na to, že se do něj nepočítá ikona šipky a proto je vždy potřeba nechat vpravo větší okraj:
select {
/* nastavíme odsazení textu a zajistíme,
aby text nelezl do ikony (16px) */
padding: 2px 20px 2px 4px;
}
Rozbalený seznam automaticky přebírá levý padding
selektu, takže zobrazí položky stejně odsazené.
Jednotlivé option
můžete v seznamu vypnout (disable
), takže pak nepůjdou vybrat, nebo je můžete úplně skrýt (display: none
). Skryté option
jdou stále nastavit programově, takže můžete do select
u vybrat hodnotu, která v seznamu není vidět. Např. můžete takto doplnit text pro prázdnou hodnotu.
Aktualizace: Novější verze Webkit a Chromium někdy zobrazují options nastavené na display: none
stejně jako disabled
. Pokud tedy chcete některé položky skutečně skrýt (a ne jen vypnout), je potřeba použít Javaskript a prvky fyzicky ze seznamu odstranit!
select > option[value=''] {
/* prázdná hodnota půjde vyplnit
programově, zobrazí svůj text,
ale uživatel ji vybrat nemůže */
display: none;
}
Pro skrytí prvků, resp. vybrání určitých prvků na základě jiného selektu nebo prvku můžete použít obdobný skript (zde příklad se zobrazením krajů pro zvolený stát:
var regions = ['CZ' => ['Praha', ...], 'DE' => ['Berlin', ...], ...];
$('#country').off('change.region').on('change.region', function () {
var
country = $(this).val(),
$region = $('#region'),
region = $region.val(),
//načtení dříve odstraněných krajů
$allRegions = $region.data('regions') || $(),
$regions = $region.find('option').not('[value=""]'),
reset = true;
;
//vloží aktuálně zobrazené regiony do skrytého seznamu
$allRegions = $allRegions.add($regions.detach());
//uloží seznam všech regionů pro pozdější použití
$region.data('regions', $allRegions);
//Vybere regiony pro aktuální stát a vloží je do selektu
$.each(regions[country], function() {
$allRegions.filter('option[value='+this+']').appendTo($region);
if (region == this) {
//pokud je aktuálně vybraný region z vybraného státu, ...
// ... není potřeba s ním nic dělat
reset = false;
}
});
//pokud je aktuálně vybraný region z jiného státu, ...
// ... je potřeba vymazat hodnotu selektu...
// ... a donutit tak uživatele zvolit platný kraj
if (reset) {
$region.val('');
}
//vypne selekt, pokud zvolený stát neobsahuje žádné kraje
// (např. pokud máme mezi státy Vatikán)
$region.prop('disabled',
$region.find('option:not([value=""]):first')
.length ? null : 'disabled'
);
//Při prvním načtení profiltruje regiony
// (skript očekává, že všechny option...
// ... budou vloženy do selektu ..
// ... pro případ, že je JS zakázané)
}).triggerHandler('change.region');
Pokud potřebujete selekt nastylovat víc, než umožňuje prohlížeč (např. v něm chcete zobrazit dvě řádky textu nebo ikony), můžete použít zástupný prvek, přes který pak zobrazíte průhledný select:
.select-wrapper {
position: relative;
}
.select-wrapper > select {
position: absolute;
z-index: 10;
top: 0;
right: 0;
left: 0;
visibility: visible;
opacity: .0001;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
border: 0;
}
Když použijete tento styl, můžete do select-wrapper
přidat libovolné HTML prvky a vlastní CSS, které pak bude zobrazeno jako váš selekt. Přitom ale vlastní hodnota bude stále uložena a změnitelná pomocí původního select
prvku, který celý wrapper překrývá a reaguje na kliknutí. Díky nastavení opacity
není vidět a je zcela průhledný, ale stále se chová jako viditelný prvek.
Select menu
Selekt menu je nový prvek, který od roku 2022 připravují týmy Microsoftu (Edge) a Googlu (Chrome). Ve výchozím stavu by mělo fungovat stejně jako starý select:
<selectmenu>
<option value=1>Option 1</option>
<option value=1>Option 1</option>
</selectmenu>
Na rozdíl od selektu ale selektmenu může obsahovat další HTML prvky (obdobně jako třeba <picture>
nebo <figure>
), pomocí nichž můžete určovat vzhled jak samotného prvku tak i rozbalovacího seznamu:
<selectmenu>
<div class=visible-part slot=button>
<button behavior=button>
<label>Hodnota:</label>
<span behavior=selected-value slot=selected-value>
Výchozí
</span>
</button>
<div class="list-part" slot=listbox>
<popup behavior=listbox>
<h3>Seznam hodnot</h3>
<option value=1>Option 1</option>
<option value=1>Option 1</option>
</popup>
</selectmenu>
Selekt menu má několik částí, které jsou volitelné, ale je potřeba je zapsat s konkrétními vlastnostmi:
- libovolný prvek s vlastností
slot=button
definuje viditelnou část prvku. To odpovídá tedy „input
“ části stávajícího selektu. - prvek
<button>
s vlastnostíbehavior=button
definuje klikatelnou část, která zobrazí seznam. To odpovídá šipce u selektu. - do libovolného prvku s vlastnostmi
behavior=selected-value slot=selected-value
se bude automaticky vyplňovat zvolená hodnota. Pokud není nastaven, použije se prvek<button>
nebo prvek s vlastnostíslot=button
. - libovolný prvek s vlastností
slot=listbox
definuje část, která ke skrytá a zobrazí se po kliku nabutton
. - prvek
popup
s vlastnostíbehavior=listbox
definuje seznam hodnot. Může obsahovatoption
aoptgroup
stejně jako stávající selekt, ale také jakékoliv jiné prvky (DIV
,Hx
,HR
, atd.)
Formátování
Díky tomu, že selektmenu může obsahovat řadu vnořených prvků, můžete je díky tomu plně formátovat v CSS použitím tagů a vlastností:
selectmenu {
//... základní styl - primárně určený pro zapnutí Flexboxu nebo Gridu
}
[slot=button] {
// ... styl pro viditelnou část
}
[slot=button} > button {
// ... styl pro klikatelnou část
}
[slot=listbox] {
// ... styl pro rozbalovací část
// opět primárně pro zapnutí Flexboxu nebo Gridu
}
[slot=listbox] > popup {
// ... styl pro viditelnou rozbalovací část
}
[slot=listbox] > popup option {
// ... styl pro jednotlivé hodnoty
}
Použití
Aktuálně (březen 2022) je selektmenu dostupné pro otestování v nejnovější verzi Chrome poté, co zapnete „Experimentální funkce“.
Dá se očekávat, že na základě ohlasů se bude ještě specifikace měnit, takže skutečný význam vlastností behavior
a slot
se může v budoucnu změnit (berte tedy zde uvedené skutečnosti s rezervou).
Osobně by mě například zajímalo, jak je to s vybranou hodnotou. Jestli třeba prvek s vlastností slot=selected-value
bude svůj obsah měnit podle obsahu zvolené hodnoty zatímco prvek s vlastností behavior=selected-value
bude měnit svůj stav :empty
podle toho, jestli je nebo není vybrána hodnota. Stejně tak jestli je po vybrání hodnoty obsah prvku se slot=selected-value
zachová v DOMu a znovu se zobrazí jako placeholder po vybrání prázdné hodnoty (např. po kliku na Reset).