CSS pseudo-třídy

Kromě CSS tříd, které sami definujete v HTML, můžete použít i pseudo-třídy, které jsou definované CSS2 a CSS3 specifikací a mají své speciální významy, které se odvíjí od obsahu HTML nebo akcí, které uživatel provádí.

Odkaz

Nejstarší a nejznámější jsou třídy pro ovládání odkazů. Třída :link označuje kotvu (anchor neboli tag A), který má uvedený HREF atribut. Stejně by se tedy dal použít selektor a[href]. Druhá třída je pak :visited, která označuje odkaz, který byl již někdy navštíven.

V moderních prohlížečích může být použití těchto tříd omezeno a často lze změnit jen barvu, podtržení nebo barvu pozadí – více viz článek o navštívených odkazech.

S odkazy souvisí ještě třetí, méně známá, třída :target. Ta označuje prvek, jehož ID je uvedeno v URL v hashi (#). To se často používá ke zvýraznění odstavce, na který se uživatel odkázal, ale použít se dá třeba i k zobrazení popup dialogu bez použití javascriptu.

Aktivní prvky

Aktivními prvky může být odkaz, na který uživatel klikl, textové pole, do kterého uživatel píše nebo checkbox, který zaškrtl.

Jako první přichází na řadu třída :hover, která se aktivuje v okamžiku, kdy uživatel na prvek najede myší nebo jiným ukazatelem (perem, joystickem, apod.). Z toho vyplývá, že nemá příliš význam u dotykových displejů a neměl by tak přinášet nějakou důležitou informaci (např. zobrazení nějaké důležité části stránky). Měl by být použit pouze ke grafickému zlepšení UX (např. interakce s menu) a nebo by měl mít alternativu (nejvhodnější je to samé, co provede :hover, udělat i po kliku, aby i mobilní uživatelé dosáhli stejného výsledku).

Když uživatel klikne, aktivuje se třída :active, která je účinná po celou dobu, co uživatel trží tlačítko myši nebo prst na obrazovce. Po dokončení kliku pak opět zmizí a dá se tak dobře použít pro zvýraznění nebo animaci kliku. Třída :active se také aktivuje, pokud uživatel najede na prvek klávesnicí (šipky nebo tabulátor) a následně stiskne mezerník nebo Enter (příp. jinou klávesu specifickou pro daný systém – na starých mobilech s joystickem nebo skrolovací dotykovou plochou to byl právě stisk joysticku nebo dotykové plochy).

Poté, co přestane účinkovat třída :active se aktivuje další :focus, která označuje prvek, který je zrovna vybraný a bude přijímat vstup z klávesnice. Fokus může prvek získat kliknutím myši, stisknutí prstem nebo najetím pomocí TAB na klávesnici.

Pozor na to, že Apple (zhruba) od iOS 11 používá nesprávně výše uvedené pseudo-třídy. Například u tlačítek probíhá proces tak, že během kliku se aktivuje :active, ale po jeho skončení zůstane pomyslný kursor na pozici kliku a začne na prvek aplikovat styl :hover, což může na dotykovém zařízení vypadat podivně a nechtěně. Pokud tedy chcete zajistit, že se :hover použije pouze na zařízeních ovládaných myší, obalte ho podmínkou @media not all and (any-hover: none) { .el:hover { ... }}. Tím ale :hover vypnete na všech zařízeních s dotykovou obrazovkou.

U checkboxu, radio a nebo option v selectu se aktivuje třída :checked u prvku, který je zaškrtnutý (checkbox) nebo zvolený (radio a option). To lze využít k označení vybrané položky nebo k zobrazení dalšího obsahu pomocí selektoru + (následující prvek):

form#adresa-doruceni { display: none; }
#checkkbox-jina-adresa-doruceni:checked
  + form#adresa-doruceni { display: block; }
/* automaticky zobrazí formulář po
   zaškrtnutí checkboxu */

Opačná třída :indeterminate se aktivuje na checkboxu, radiogroup nebo selectu, pokud nemá zvolenou výchozí hodnotu (u checkboxu je to atribut indeterminate=true, u ostatních pak nepřítomnost atributu selected). Některé prohlížeče ale mohou výchozí hodnotu vybrat sami a tak se nemůžete na tuto třídu spoléhat nebo může být potřeba zásah javaskriptem pro vymazání výchozí hodnoty.

Formuláře

Kromě již uvedených :focus a :checked můžete ve formuláři použít i další pseudo-třídy.

Třída :default odpovídá tlačítku, které se aktivuje po stisku Enter. Zpravidla to je první tlačítko Odeslat (input[type=submit]) ale tato pseudo-třída je univerzálnější a může reagovat i na jiné speciální situace. A měla by být rychlejší, protože prohlížeč nemusí ověřovat ostatní podmínky selektoru.

Třída :disabled má stejný význam jako selektor [disabled], ale je o něco rychlejší při vyhodnocování. Opačná třída :enabled pak platí pro všechny prvky, které nemají atribut disabled (:not([disabled])) a je opět rychlejší než ostatní kombinované selektory. Obdobně fungují i třídy :read-only a :read-write, které sledují atributy readonly u inputů a contenteditable u ostatních prvků.

Pro validaci formuláře můžete použít třídy :required, která odpovídá prvku s atributem required (selektor [required]). Opakem je pak třída :optional, která platí pro všechny ostatní prvky.

Pro označení správnosti hodnoty slouží třídy :valid a :invalid, které ale mají význam jen u automaticky validovaných inputů jako je number, url, email, apod.

Pro input typu number pak můžete použít pseudo-třídu :in-range, která bude aktivní jen v případě, že prvek obsahuje (validní) hodnotu mezi min a max hodnotami. Tato třída má význam jen pokud jde do pole psát na klávesnici – poskytnutými šipkami by totiž nikdy hodnota neměla povolené hranice opustit. Opačná třída :out-of-range pak nahrazuje selektor :not(:in-range).

Další podivně podporovaný je pak selektor ::placeholder, který lze použít u inputů pro formátování textu placeholderu. Ve Firefoxu je potřeba použít dva prefixy :-moz-placeholder a ::-moz-placeholder (dvojtečka funguje až od verze 18), pro Webkit/Chromium je dokonce potřeba použít ::-webkit-input-placeholder a pro Edge zase ::-ms-input-placeholder.

Další ještě méně podporovaný (jen ve Webkit/Chromium od roku 2016 a Firefox od 2017) je selektor :placeholder-shown, který vybírá celý input (ne jen placeholder), ale pouze v případě, že input skutečně zobrazuje placeholder. Toto pravidlo tedy přestane platit v okamžiku, kdy input obsahuje hodnotu nebo je momentálně aktivní (zobrazuje kurzor – v tom se ale některé prohlížeče rozcházejí a aplikují styl i na prvek s kurzorem, ale prázdným textem).

Poznámka 1: ve skutečnosti se posledně uvedený selektor prvně objevil o 5 let dříve ve Firefoxu jako :-moz-placeholder a v IE jako :-ms-input-placeholder. Z Edge byl ale následně odebrán a nahrazen až po přechodu na Chromium jádro.

Poznámka 2: různé zdroje se rozcházejí v tom, jestli (všechny výše uvedené) selektory pro placeholdery mají mít jednu nebo 2 dvojtečky. Zde uvedené selektory jsou podle caniuse stránky, nicméně doporučuji si každý selektor otestovat nebo pro jistotu uvést oba.

Update: Od roku ~2023 by se dala podpora obou základních placeholder selektorů považovat za dostatečnou (97% uživatelů podle caniuse) na to, že ji můžete plně využívat (v podstatě i bez prefixů).

Prvky na stránce

Další sada pseudo-tříd reaguje na různé situace v HTML a má tak význam jen pro kód generovaný na serveru nebo v javaskriptu (pokud píšete HTML ručně, zpravidla můžete dopsat i příslušné CSS třídy nebo ARIA atributy).

Základní třída :root odpovídá hlavnímu prvku na stránce, což je <HTML> v HTML stránce, ale může to být i jiný, pokud označuje část SVG obrázku nebo jiného XML kódu. Rozdíl je v tom, že selektor html je tag a má tedy menší prioritu než selektor třídy :root.

Pseudo-třída ::selection (se dvěma dvojtečkami) odpovídá části stránky, která je vybrána (označena). Můžete tak změnit barvu textu a pozadí výběru.

Pořadí prvku

Následuje skupina tříd, které vybírají prvky podle jejich pozice v HTML. Třídy :first-child a :last-child vybírají vždy první a poslední prvek v daném kontejneru. Třídy :first-of-type a :last-of-type pak vybírají první a poslední prvek daného typu. Pokud tedy máte v odstavci SPANy, EMy a STRONGy, selektor „p *:first-of-type“ vybere tři prvky v každém odstavci – první SPAN, první EM a první STRONG.

Trochu složitější alternativy jsou :nth-child, :nth-last-child, :nth-of-type a :nth-last-of-type, které místo prvního či posledního mohou vybrat libovolný prvek nebo i více prvků na základě jejich pořadí v kontejneru. To se vždy uvádí do závorek za pseudo-třídu:

:nth-child(2) { /* druhý prvek */ }
:nth-child(5) { /* pátý prvek */ } 

:nth-child(2n) { 
    /* každý druhý, 2., 4., 6., atd. */ }
:nth-child(n+5) { 
    /* všechny počínaje pátým, 5., 6., 7., atd */ } 
:nth-child(-n+5) {
    /* prvních 5, tedy 1., 2., 3., 4. a 5. */ } 
:nth-child(2n+5) {
     /* každý druhý počínaje pátým,
        tedy 5., 7. 9, 11., atd. */ } 

:nth-child(odd) { /* liché prvky */ } 
:nth-child(even) { /* sudé prvky */ } 

:nth-child(n+4):nth-child(-n+8) {
     /* čtvrtý až osmý prvek,
        tedy 4., 5., 6., 7., a 8. */ } 
:nth-child(2n+4):nth-child(-2n+8) {
     /* každý druhý mezi čtvrtým a osmým prvkem,
        tedy 4., 6. a 8. */ } 

img:nth-of-type(2n) { /* každý druhý obrázek */ }

Slova even (sudý) a odd (lichý) jsou jen náhražky za matematicky vyjádřené 2n (každý druhý) a 2n+1 (každý druhý počítáno od prvního).

Selektory *-of-type dávají větší význam u těchto počtů, kde např. můžete označit každý druhý label, i když máte kombinace label + input a checkbox + label a nelze tedy spoléhat na to, že label bude vždy lichý nebo sudý prvek v kontejneru.

Selektory *-last-* vždy jen obrací počítání od posledního prvku, všechny ostatní výpočty zůstávají stejné.

Pozor na to, že pro výpočet pořadí se uvažují i skryté prvky, takže pořadí „každý druhý“ nebude fungovat správně, pokud je třetí prvek skrytý a pro uživatele bude zvolený 2., 3., 5., atd. (protože viditelný třetí je ve skutečnosti čtvrtý, atd.).

Někde můžete najít ještě zápis se záporným posunem. např. 2n-3, ale to je jen jinak zapsaný matematický vzorec (např. 2n-3 odpovídá 2n+1) a dost často ani nefunguje správně.

Počet prvků

Další skupina pseudo-tříd se zaměřuje na počet prvků v kontejneru. Třída :empty (prázdný) platí je pro prvky, které nemají žádné potomky. Pozor na to, že i prvek, který obsahuje mezeru, není prázdný! První, co vás asi napadne, je takový prvek skrýt, pokud jde třeba o prázdný kontejner pro obrázky nebo prázdný seznam uživatelů.

Pokud kontejner obsahuje jen jeden prvek, daný prvek bude mít pseudo-třídu :only-child. Pokud má kontejner více prvků, ale jen jeden daného typu (např. SPAN), bude mít takový prvek třídu :only-of-type. Opět se hodí pro označení seznamu, který má jen jeden prvek a tedy není třeba řešit např. rozestup mezi prvky. Nebo můžete takový obrázek zobrazit na celou šířku místo počítání procentuální šířky.

Pokud potřebujete určit styl pro prvky, jichž je víc v kontejneru, můžete k tomu použít třídy :nth-child a :nth-last-child. Pokud jsou totiž v kontejneru např. 3 prvky, znamená to, že první prvek je zároveň 3. od konce a naopak 3. prvek je 1. od konce. Díky tomu můžete např. nastavit šířku prvků podle jejich počtu:

.gallery > img:nth-child(1):nth-last-child(1) {
    width: 100%; /* styl pro 1 prvek */
    /* uvedeno pro úplnost, stejně lze použít
       třídu :only-child */ } 

.gallery > img:nth-child(1):nth-last-child(2), 
.gallery > img:nth-child(2):nth-last-child(1) {
     width: 50%; /* pro 2 prvky */ } 

.gallery > img:nth-child(1):nth-last-child(3),
.gallery > img:nth-child(2):nth-last-child(2), 
.gallery > img:nth-child(3):nth-last-child(1) {
     width: 33.333%;
     width: calc(100% / 3); /* pro 3 prvky */
}
/* atd. */

Pro větší počty prvků v kontejneru můžete použít vylepšení od Lea Verou, které využívá selektor sourozenců (~):

/* pro 10 prvků v kontejneru */
.gallery
   > img:first-child:nth-last-child(10),
.gallery
   > img:first-child:nth-last-child(10)
      ~ img {
     width: 10%;
}

Pravidlo říká: „vyber obrázek, který je první a zároveň 10. od konce (tedy první obrázek z deseti), a k tomu ještě vyber všechny jeho sourozence (tedy 2. až 10. obrázek).“

Ještě lepší zápis by byl pomocí SASS nebo LESS, kde můžete použít cyklus, abyste nemuseli psát vše ručně:

/* SCSS zápis pro 1 až 10 prvků */ 
.gallery {
   @for $i from 1 through 10 {
     img:first-child:nth-last-child(#{$i}),
     img:first-child:nth-last-child(#{$i})
        ~ img {
       width: #{100/$i}%
     }
   } /* for */ 
} /* .gallery */

Dodané prvky

Pomocí CSS můžete ve stránce vytvářet i nové prvky a to pomocí CSS2 selektorů :before a :after nebo jejich CSS3 alternativ se dvěma dvojtečkami ::before a ::after (mají stejný význam, ale CSS2 verze je lépe podporována na starých prohlížečích). S těmito pseudo-třídami souvisí CSS atribut content, pomocí kterého dáte prvku obsah a donutíte ho zobrazit se ve stránce.

Text

Na pořadí textu místo pořadí prvků cílí třídy :first-letter a :first-line, které můžete opět psát se dvěma dvojtečkami v CSS3, tedy ::first-letter a ::first-line.

Třída :first-letter funguje na všechny prvky, které obsahují text, dokonce i na dodané prvky :before a :after. Třída :first-line ale funguje jen na blokové prvky (DIV, P, H1, atd.) a nemá účinek u inline prvků (SPAN, EM, STRONG, atd.).

Text také můžete ovlivnit pseudo-třídou :lang(), která se nastavuje podle kombinace hlavičky Content-Language a atributu lang. Např. označit cizojazyčné výrazy lze kombinací HTML a CSS:

<p lang=cs>Pro <span lang=en>layout</span>
stránky je nejdůležitější 
<span lang=de>gestalt</span> pravidlo
a <span lang=jp>boken</span> pozadí.</p>
 
:lang(cs) { color: black; } 
:lang(en) { color: red; } 
:lang(de) { color: blue; } 
:lang(jp) { color: green; }

Poznámka: layout je rozložení stránky, pravidlo gestalt (z němčiny „tvar“) se týká podobnosti a rozdílnosti prvků na stránce (např. všechny inputy musí vypadat stejně). Výraz bokeh (暈け, často též ボケ, z japonštiny „rozmazané“) označuje nevýrazné, nekontrastní (a často, hlavně ve fotografiích, skutečně rozmazané) pozadí (tedy pozadí, na kterém vyniknou důležité prvky stránky).

Při definici jazyka je potřeba dát pozor na to, že zatímco v HTML4 je nejpoužívanější způsob definice jazyka <meta http-equiv="Content-Language" content="cs">, tak v HTML5 je tento způsob zakázaný a prohlížeče ho ignorují. Pokud tedy váš server přímo neposílá správnou HTTP hlavičku Content-Language, je potřeba jazyk určit pro jednotlivé prvky, nejlépe pro HTML nebo BODY:

<HTML lang="cs-CZ">
    <BODY>
        <aside lang="en-US">In English</aside>
        <main>...

Jazyk můžete určit buď dvouznakovým kódem jazyka (např. cs) nebo ho doplnit o dvouznakový kód státu (např. en-US), kdy je potřeba jazyk a stát oddělit pomlčkou.

Dalšího vylepšení může dosáhnout kombinací :lang s pseudo-třídami :before a :after a vlastnosti content a uvést tak cizojazyčné výrazy do uvozovek (např. anglické do „uvozovek“, německé do »šipek« a japonské do 「japonských uvozovek」).

Pokud chcete podle jazyka měnit uvozovky citací (q a blockquote), můžete využít CSS vlastnost quote, která definuje, jaký znak se použije pro levou a pravou uvozovky. Pokud uvedete 4 znaky, budou použity pro více-úrovňové citace. Pro uvozovky je doporučeni používat UNICODE hexa kódy, aby nedošlo k záměně při ukládání souborů nebo špatnému formátování, protože sami uvozovky musí být uvedeny v uvozovkách:

<p lang=cs>Jára Cimrman řekl o externismu:
<q>Albert Einstein považoval moji teorii
za <q lang=en>very crazy</q></q></p>
//využití jazyka pro definici uvozovek
q:lang(cs) { quotes: "\201E" "\201D"; }
q:lang(en) { quotes: "\00AB" "\00BB"; }

//definice vnořených uvozovek (bez jazyka)
p { quotes: "\00AB" "\00BB" "\2039" "\203A"; }

Citát při použití prvního stylu podle jazyka bude „Albert Einstein považoval moji teorii za «very crazy»”, při použití stylu s vnořenými citacemi pak «Albert Einstein považoval moji teorii za ‹very crazy›».

Template sloty

Pokud používáte TEMPLATE tagy ve stránce, hodnoty do nich vkládáte tagem SLOT (více viz Použití templatů). Pokud v CSS potřebujete naformátovat vložený prvek, můžete to udělat pomocí funkce ::slotted():

::slotted(*) {
	outline: 1px solid green;
}
:not(::slotted(*)) { //viz dále
	outline: 1px solid red;
}

Pozor na to, že ::slotted() lze použít POUZE uvnitř stínového DOMu, takže výše uvedené třídy není možno vytvořit pomocí debuggeru a je potřeba je zapsat do stylu přímo v template (v debuggeru klikněte na template, z menu zvolte Upravit jako HTML a vložte daný styl):

<template>
    <style>
        ::slotted(*) { outline: 1px solid green; }
    </style>
    <!-- template... -->
</template

Funkci ::slotted() lze použít POUZE na prvek typu SLOT, takže ve většině případů stačí uvést jen funkci. Je potřeba si ale uvědomit, že funkce ::slotted() neformátuje slot, ale pouze prvek v něm vložený (selektor uvedený v závorce).

//Plný zápis
article slot[name=title]::slotted(h2.news) { ... }

//zkrácený zápis - H2 může být pouze titulek
article ::slotted(h2.news) { ... }

Pozor na to, že funkce slotted popisuje současně dva prvky (slot – levá část – a vložený prvek – v závorce) a proto u některých frameworků (LESS a SASS) je potřeba správně používat zástupné znaky („&“); např. &::slotted(*) vs. ::slotted(&), což jsou dva zcela odlišné zápisy – první popisuje prvek vložený do slotu určeného nadřazeným selektorem, zatímco druhý naopak říká, jak má vypadat zvolený prvek, pokud je vložený do slotu. Obvyklý zápis slot { &, &::slotted() { ... }} obvykle nedává smysl, protože slot a do něj vložený prvek jsou dva odlišné prvky a aplikovat na ně stejný styl nedává smysl (kromě specifických případů, třeba ve flexbox layoutu, kde chcete, aby každý prvek flexboxu byl opět flexbox).

Negace

Třída :not(), která obrací význam selektoru, který je uveden v závorkách, je pochází z CSS3, ale v CSS4 byla vylepšena společně s dalšími novými pseudo-třídami (viz dále). Podle CSS3 může ve třídě :not() být uveden pouze jediný selektor, kterým může být ID (:not(#menu)), třída (:not(.listitem)), jiný selektor (:not([selected])) nebo i jiná pseudo-třída (:not(:default)).

Podle CSS4 (podporováno od Firefox 78 (červen 2020), Safari 14 (září 2020), Chrome a Edge 88 (leden 2021) a odpovídající mobilní verze) může :not() obsahovat i složitější selektory jako je výběr potomka nebo sourozence (např. a:not(>img) pro výběr odkazu, který neobsahuje obrázek nebo div:not(+div) pro výběr DIVu, za nímž nenásleduje další DIV) nebo více selektorů upřesňujících, co chceme vyloučit (např. div:not(.note.red) pro výběr DIVu, který nemá třídy note a red). Třída :not() může dokonce obsahovat čárkou oddělený seznam selektorů, které pak fungují jako OR (např. div:not(.note, .spacer, .hidden, #menu) pro výběr DIVů, které nemají žádnou ze tříd note, spacer ani hidden nebo nemají id menu).

Pamatujte ale na to, že některé pseudo-třídy již mají svůj opak (:required a :optional, :first-child a :nth-child(n+2), atd.), které jsou rychlejší a často i čitelnější (tedy kromě :not(:first-child)).

U normálních selektorů pak některé kombinace nemusejí dávat smysl, např. #menu:not(#page) nemůže mít dvě ID, nebo div:not(span), protože žádný prvek nemůže mít dva typy.

Na druhou stranu uvedením nesmyslných podmínek můžete zvýšit prioritu pravidla, např. #menu:not(#page) obsahuje dvě ID a proto má vyšší prioritu než obyčejné pravidlo #menu a je rychlejší než uvádění cesty html > body#page > #menu.

Vnořený OR selektor

Specifikace CSS4 přidává jeden velmi užitečný selektor, který nahrazuje vnořování selektorů v preprocesorech SASS a LESS.

Selektor :is() může obsahovat čárkou oddělený seznam selektorů, přičemž následně vybere všechny prvky, který pasují na alespoň jeden z nich. Například :target:is(h1, h2, h3, h4, h5, h6) vybere cíl odkazu (kotva neboli URL hash), ale jen pokud odkazuje na libovolný nadpis. Třída :is() je tzv. syntactic sugar, protože stejného efektu lze dosáhnout zápisem h1:target, h2:target, atd., ale výše uvedený zápis je kratší a je z něj jasnější, co chcete udělat.

Selektor :is() je podporován od Firefox 78 (červen 2020), Safari 14 (září 2020), Chrome a Edge 88 (leden 2021) a v odpovídajících mobilních verzích. Nicméně většina prohlížečů (kromě IE a starého Edge) podporuje i dřívější návrhy specifikace a tak místo :is() můžete použít :-webkit-any() (Chrome 15+, Opera 15+, Android 4+, Safari 5.1+, iOS 7+, Edge 79+) nebo :-moz-any() (Firefox 4+). Safari 9+ ještě podporuje třídu :matches(), která funguje stejně jako :-webkit-any() a :is().

Problém je v tom, že pokud byste chtěli podporovat všechny 3 verze pro nové (2020+) i starší prohlížeče, bude zápis delší, než kdybyste ho rozepsali, a nic neušetříte (např. :target:is(h1, h2, h3, h4, h5, h6), :target:-webkit-any(h1, h2, h3, h4, h5, h6), :target:-moz-any(h1, h2, h3, h4, h5, h6)). Ještě pár let tedy bude jistější používat prekompilátory, které tohle umí dělat mnohem efektivněji a navíc podporují i IE a Edge (např. :target { h1, h2, h3, h4, h5, h6 { ... } }).

Výběr rodiče

Další pseudo-třída, kterou přidává CSS4 specifikace je :has(), která umožňuje stylovat rodiče podle toho, jaké má potomky (tedy jaké prvky jsou uvnitř něho).

Problém je, že tato třída zcela obrací způsob, kterým programátoři a prohlížeče stylují HTML prvky a proto také zatím není podporován v žádném prohlížeči. Důvodem je nejspíše to, že použití této třídy by výrazně zpomalilo vykreslování stránky.

Aktualizace: Safari od verze 15.4 (duben 2022) podporuje selektor :has díky tomu, že Webkit tým vymyslel způsob, jak optimalizovat aplikaci stylu na rodiče bez toho, aby bylo nutné čekat na načtení celé stránky nebo ji do nekonečna překreslovat. Tohle je celkem nečekaná zpráva, jelikož Safari bylo po mnoho let považováno odpadlíka, který nové funkce implementuje s několikaletým zpožděním oproti ostatním prohlížečům (Chromium a Mozilla). Od verze 106 (září 2022) podporují selektor i Chromium prohlížeče pro Windows (Chrome, Edge a Opera) a Android (Chrome). Firefox na Windows od verze 103 (červen 2022) ho podporuje jako experimentální funkci.

Příklady použití:

  • a:has(>img) = odkaz, který obsahuje obrázek na první úrovni potomků,
  • li:has(>a) = položka seznamu, která je současně odkazem,
  • figure:has(figcaption) = vnořený prvek (obvykle obrázek), který má titulek,
  • p:has(span, b, i, strong, em) = odstavec, který obsahuje nějaké vnořené formátovací prvky (SPAN, STRONG, atd.),
  • main:has(article) = stránka obsahující alespoň jeden článek.

Vysvětlení (důvodu, proč nebyl selektor dlouho implementován): V posledním příkladu je vidět, proč žádný prohlížeč v minulosti tuto třídu nepodporoval. Představte si, že by nezodpovědný programátor napsal styl html:has(a), body:has(a) – prohlížeč by musel nejprve počkat na načtení a zpracování celého HTML a následně ho dvakrát celé projít, jen aby ověřil, jestli obsahuje alespoň jeden odkaz, aby podle toho mohl nastylovat kořenové prvky HTML a BODY. Něco takového je v CSS v podstatě nepřípustné a zcela by to vyloučilo použití všech optimalizací, které vývojáři do prohlížeců vložili.

CSS4 specifikace navíc uvádí, že je možné vnořovat do sebe třídy :has() a :not() pro výběr prvků, které neobsahují určitého potomka (např. article:not(:has(h3, h4, h5, h6)) pro výběr článku, který obsahuje jen jednu úroveň nadpisů (protože H1 by mělo být použito pro titulek stránky a ne jako nadpis článku)).

Nejzákladnější použití je nastylování nějakého prvku podle jeho volitelného obsahu, například tlačítko, které obsahuje kromě popisku navíc i ikonu:

button { 
    /* ... styl pro všechna tlačítka  */
}
button:has(img, picture, svg) {
    /* ... style pro tlačítka s ikonou */
}

Stylovat můžete i větší kontejnery a dokonce i v závislosti na stavových pseudo-třídách. Pro příklad si představte formulář pro adresu s pevnou výškou – pokud uživatel zaškrtne checkbox „Nakupuji na firmu“, nejenže musíte zobrat pole pro zadání jména firmy a IČO, ale také musíte změnit výšku formuláře:

form {
    height: 200px;
}
form:has(input[type=checkbox][name=company]:checked {
    /* selektor je záměrně napsaný složitější než je potřeba,
        abych ukázal, na který přesně prvek form reaguje */
    height: 300px;
}

Stejně tak můžete has použít pro zvýrazmění validních nebo nevalidních prvků formuláře:

form-group:has(:invalid) {
    outline: 1px solid red;
}
form-group:has(:valid) {
    outline: 1px solid lime; //světle zelená
}
form-group:has([required]::placeholder-shown) {
    //required prvek s placeholderem
    //není validní, protože je prázdný
    //(ale obvykle nemá :invalid stav)
    outline: 1px solid red;
}

form-group:has([required]::placeholder-shown)
   > label 
{
    //zobrazí červený label pro vyžadované položky
    color: red;
}
form-group:has(:invalid) > span.error {
    //zobrazí chybovou hlášku pro nevalidní inputy
    display: inline;
}

V ultimátním případě můžete změnit rozložení celé stránky napsané pomocí CSS Grid z závislosti na tom, které prvky obsahuje bez nutnosti nastavovat nějaké třídy (např. pokud je uživatel přihlášený nebo ne):

/* Styl gridu podle poču prvků */
main.grid:has(:nth-child(2):last-child) {
    /* ...styl pro stránku se 2 prvky - menu a obsah */
    grid-template-columns: 1fr 2fr;
}
main.grid:has(:nth-child(3):last-child) {
    /* ...styl pro stránku se 3 prvky - menu, obsah a reklamy */
    grid-template-columns: 2fr 4fr 1fr;
}
main.grid:has(:nth-child(4)) {
    /* ...styl pro stránku se 4 a více prvky
         např. seznam článků nebo galerie */
    grid-template-columns: 1fr 1fr 1fr 1fr;
   /* 4 sloupce - 5. a další prvek se zalomí na další řádky */
}

Selektor :has nemusí sloužit jen pro výběr potomka, ale může měnit styl prvku podle toho, který prvek je před ním:

p:has(+h2, +h3, +h4) {
    /* Styl pro pro první odstavec po nadpisu */
    margin-top: -0.5em; /* zmenší mezeru od nadpisu */
}
label:has(+input[type=checkbox) {
    /* styl pro label uvedený za checkboxem */
    margin-left: 0.5em; /* přesune margin zprava doleva */
    margin-right: 0;
}

Stylování modálního okna

HTML5 definuje nový HTML element <dialog> (plná podpora ve všech prohlížečích je zhruba od března 2022), pomocí kterého můžete část stránky zobrazit jako modální okno (aka popup nebo overlay). Dříve se podobná okna zobrazovala pomocí odkazu na kotvu (href="#modal") a stylovala pomocí pseudo-třídy :target.

S dialogem toto již není možné a tak existuje nový selektor :modal, který začne u prvku platit, když se zobrazí (tzn. má atribut open). Můžete tak stylovat dialog v okamžiku, kdy se zobrazí a díky :has také celou stránku, když je dialog zobrazený:

dialog:modal { 
    //nastaví velikost a pozici dialogu
    display: block;
    width: 300px;
    height: 200px;
    margin: 1em auto;
}

html:has(:modal) {
    //vypne skrolování stránky,
    //když je zobrazený dialog
    overflow: hidden;
}

Další styl, který souvisí s dialogem a modalem je ::backdrop, pomocí kterého můžete určit styl pozadí dialogu (které se obvykle dělá šedivé a poloprůhledné):

dialog::backdrop {
    background: rgba(128,128,128,0.25);

Pozor na to, že dialog není jediný prvek, na který se může aplikovat :modal a ::backdrop. Například video zobrazené ve fullscreen bude také modální a bude mít svůj backdrop (viz Fullscreen API).

Modal a backdrop jsou plně podporovány od srpna 2022 … pouze v Safari je potřeba použít ::-webkit-backdrop, protože tam je takto podporování již od roku 2015!

Podpora

Podpora v prohlížečích je u některých stoprocentní (jako :link a :focus), některé jsou jen u novějších (:before a :nth-child), nebo vyžadují prefixy (třeba :-moz-selection) nebo dokonce jiné názvy (:placeholder nebo :-*-input-placeholder). Některé zatím podporovány nejsou, i když jejich podpora postupně přibývá (např. :modal, :fullscreen a ::backdrop byly dlouho nedostupné a byly přidány až v letech 2020 – 2022).

Na detekci podpory selektoru nemůžete použít pravidlo @supports(), ale i tak je možnost podporu detekovat.

Pokud totiž do jednoho pravidla zkombinujete více selektorů a jeden z nich nebude podporován, prohlížeč zahodí celé pravidlo. Pokud tedy k podporovanému selektoru přidáte další, který chcete otestovat, ale který napíšete tak, že nic nevybere, můžete nastavit styl prvku (vybraným prvním selektorem) jen v případě, že prohlížeč podporuje ten druhý:

Pro příklad budeme chtít v tabulce odlišit barvy sudých a lichých řádek, abychom nemuseli používat okraje, ale v prohlížečích, které nepodporují :nth-child() budeme muset zobrazit okraje buněk:

/* styl border pro prohlížeč 
   bez podpory nth-child */
table td { border: 1px dotted gray; }

/* styly pro sudé a liché řádky */ 
table tr:nth-child(2n+1) 
    { background: white; } 
table tr:nth-child(2n) 
    { background: gray; } 

/* zruší border (z prvního stylu),
   když prohlížeč podporuje nth-child */ 
.not(*):nth-child(2n), 
table td { border: 0 none; }

Prohlížeč, který nezná :nth-child() bude ignorovat druhé a třetí pravidlo pro nastavení barvy. Aby nezrušil border pomocí 4. pravidla, přidali jsme do selektoru také :nth-child(), takže ho také přeskočí jako neznámý selektor.

Naopak prohlížeč podporující :nth-child() použije 2., 3. i 4. pravidlo a nastaví barvy a zruší border. Aby ale 4. pravidlo nezrušilo border i nějakým nechtěným sudým prvkům, použili jsme záporný selektor „ne všechno„, který logicky nevybere nic – kombinace s :nth-child() už je pak irelevantní a slouží jen pro detekci podpory.

Aktualizace: Novější prohlížeče (2022+ – podpora byla přidána hlavně kvůli selektoru :has()) umožňují kombinaci klíčových slov @supports selector(X) pro otestování, zda prohlížeč ví, jak vybrat prvky pomocí uvedeného selektoru. Starší prohlížeče, které @supports selector(X) nepodporují samozřejmě všechna pravidla uvnitř bloku zahodí jako neznámou syntaxy:

/* Pokud prohlížeč dokáže vybrat
   cokoliv pomocí pseudo-třídy :has... */
@supports selector (:has(*)) {
    /* ... použij :has() pro stylování rodičů */
    button:has(img) { ... }
    main:has(.login) { ... }
}

Omezený styl

V některých prohlížečích funguje speciální selektor :scope, který ale není přesně specifikovaný a tak není jasné, jak přesně funguje.

Navíc v některých nových verzích prohlížečů, které ho dříve podporovaly, byl zpětně odebrán, takže nemá ani cenu se jím zabývat.

Pokud potřebujete omezit styl na určitý blok kódu, musíte použít vnořený selektor (aka selektor s cestu k prvku) nebo BEM:

/* omezení stylu 
   na Login form pomocí vnoření */
.login-form input { color: gray; } 

/* omezení pomocí BEM */ 
.login-form__input { color: gray; }

Jaký je mezi pravidly rozdíl si přečtěte v článku Optimalizace selektorů.

1 komentář u „CSS pseudo-třídy“

  1. Upozornění: jak je v článku uvedeno, třída :lang() reaguje na jazyk nastavený pro daný prvek. Nicméně v HTML5 je použití META atributu s vlastností Content-language zakázáno a prohlížeče ho ignorují.
    Pro správné nastavení jazyka pro celou stránku v HTML5 je potřeba použít HTTP hlavičku Content-language odeslanou serverem, nebo atribut lang na HTML prvku (např. <HTML lang=“cs-CZ“>).

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Tato stránka používá Akismet k omezení spamu. Podívejte se, jak vaše data z komentářů zpracováváme..