Už vás někdy napadlo, jak udělat např. číslování kapitol v dokumentu bez toho, abyste měli celý dokument uzavřený v jednom velkém OL-LI seznamu? A určitě jste pak jen mávli rukou: „To udělám přes jQuery.“ Ale víte, že to jde bez skriptu, jen pomocí CSS?
To je nějaká novinka?
CSS vlastnost counter je již definována v CSS 2.1 a nedá se tedy říci, že by šlo o nějakou novinku. A to je možná důvod, proč se o ní neví, protože teprve s CSS 3 přišla různá formátovací kouzla a vychytávky a vývojáři se teprve začali více zajímat o všechny „nové“ možnosti (a narážet na to, kde všude nefungují). Naproti tomu je counter podporován ve všech prohlížečích včetně starých Explorerů.
Počátky
Běžně si pro očíslované či neočíslované seznamy (resp. z anglického ordered/unordered lists správně řazené a neřazené seznamy) vystačíte s HTML:
Očíslovaný seznam získáte pomocí:
<ol>
<li>HTML tagů
<li>CSS listů
<li>CSS counterů
</ol>
A pokud nechcete používat LI, stačí změnit CSS:
.list {
display: list-item;
list-style-type: decimal;
}
Nevýhoda těchto způsobů je v tom, že nemáte příliš kontrolu nad tím, jak se budou prvky číslovat a pokud se seznam jakkoliv přeruší, začne opět od jedničky.
A právě kvůli tomu existuje counter:
.list-start {
counter-reset: seznam 0;
}
.list-item:before {
counter-increment: seznam 1;
content: counter(seznam) '.'
}
Uvedený kód provádí následující:
- Nejprve na začátku seznamu nastaví počet na nulu.
- Pak, před tím, než vykreslí položku seznamu, zvedne počet o 1.
- A nakonec vypíše počet a za ním tečku (např.: 1., 2., 3., atd.)
Poznámka: Česky se counter překládá jako čítač, ale dá se použít i označení počítadlo. V textu budu používat anglický výraz, protože tím myslím přímo danou CSS vlastnost.
Použít se to dá například pro číslování kapitol a podkapitol:
body { counter-reset: kapitola }
h1:before {
counter-increment: kapitola;
content: count(kapitola);
counter-reset: podkapitola;
}
h2: before {
counter-increment: podkapitola;
content:
counter(kapitola)
'.' counter(podkapitola);
}
Vygenerované nadpisy pak mohou vypadat:
1. úvod 2. stať 2.1 Slovo Úvodem 2.2 Vo co tu de? 2.3 Na závěr 3. Závěr
Syntaxe
CSS counter se skládá ze dvou vlastností a dvou funkcí, které můžete použít ve vlastnosti content (která vkládá do dokumentu text).
V příkladech budu používat smyšlenou hru, která prvkům na stránce přiřazuje CSS třídy a prohlížeč pak automaticky počítá body pomocí CSS counterů.
counter-increment
Nejdůležitější je vlastnost counter-increment, pomocí které počítáte prvky. Funguje tak, že pokaždé, když prohlížeč vykresluje element mající tuto vlastnost, zvýší daný counter o zadanou hodnotu:
.get-1-point { counter-increment: points; }
.get-2-points { counter-increment: points 2; }
.lose-3-points { counter-increment: points -3; }
Pomocí jednoho prvku lze zvýšit i více counterů jednoduše tím, že je uvedete za sebe. Může zadat i počet, přičemž countery a inkrementy se nijak neoddělují (vyjma mezery), prohlížeč vyhodnotí zápis jen na základě toho, že counter je jméno (A-Z) a inkrement je číslo:
.get-1-point {
counter-increment: points collected;
}
.get-2-points {
counter-increment: points 2 collected;
}
.lose-3-points {
counter-increment: points -3 collected -1;
}
.get-5-points {
counter-increment: points 2 points 3;
} /* uvedení stejného counteru vícekrát
započte všechna uvedená zvýšení */
Pozor na to, že stejně jako všechny ostatní CSS vlastnosti se i counter přepisuje (správně kaskáduje z názvu Cascade Style Sheets) – pokud tedy jednomu prvku definujete více counter-increment vlastností, bude se brát v úvahu pouze ta poslední:
.get-1-point {
counter-increment: points collected;
counter-increment: points 2;
} /* zvýší points o 2,
ale collected nezvýší vůbec!!! */
/* -------------------------- */
.get-2-points {
counter-increment: points 2;
}
.get-2-points.double {
counter-increment: points 4;
}
.get-2-points.dummy {
counter-increment: points 0;
} /* Double zvýší o 4, a Dummy nezvýší o nic */
/* -------------------------- */
li { counter-increment: odrazky; }
ul * { counter-increment: odrazky 0; }
/* nikdy nezvýší counter odrazky, protože vždy
vyhraje styl 'ul *' */
counter-reset
Pomocná vlastnost je counter-reset, pomocí které můžete nastavit výchozí hodnotu counteru nebo ho nastavit na pevně dané číslo.
I když prohlížeče dosud nepoužitý counter považují za nulový, CSS specifikace i programátorská doporučení říkají, že „každý čítač by se měl před použitím nastavit na 0„. Pokud k tomu nemáte žádné specifické místo, je dobré použít BODY, které je v každém dokumentu jen jednou a vykresluje se jako první:
body {
counter-reset: points;
}
Lze samozřejmě použít i nějaké výchozí hodnoty:
.bonus-game { counter-reset: points 100; }
Zresetovat můžete i více counterů stejně jako je inkrementujete – zde platí i všechna pravidla a upozornění o překrývání pravidel apod.:
.bonus-game {
counter-reset: points 100 collected;
}
.hard-game {
counter-reset: points -100 collected -25;
}
counter()
Počítání je hezké, ale pokud nevidíte výsledek, tak je to k ničemu. K tomu slouží funkce counter(), kterou můžete použít ve vlastnosti content:
.debug .game-item:before {
content: counter(points);
}
.results-points:after {
content: counter(points) ' points';
}
.results-text:after {
content: 'Your ' counter(collected)
' items earned you ' counter(points)
' points';
}
V content můžete používat i více funkcí nebo řetězců. Ty se oddělují pomocí mezery (nepoužívejte žádné „+“ nebo „.“ známé z JS nebo PHP). Nezapomínejte, že oddělovací mezery se nepočítají, takže mezery ve výsledném textu musíte vkládat do uvozovek.
Formát
Pomocí funkce counter() můžete měnit i formát vypisovaných čísel stejně jako můžete měnit běžný očíslovaný seznam.
.last-letter:after {
content: counter(letters, upper-latin);
}
Použít lze všechny formáty: čísla (decimal) včetně úvodní nuly (decimal-leading-zero), římské číslice (lower-roman, upper-roman), písmena (lower-latin, upper-latin) či řecká písmena (lower-greek). Použít lze dokonce i odrážky (disc, circle, square), ale ty se vždy zobrazí pouze jako jeden znak (i když counter je 0), takže jejich použití je sporadické – jedině snad pro zobrazení odrážky u prvku, který není display:list-item:
a { display: block; }
a:before { content: counter(null, 'disc'); }
Vnořování
Jak jsem uvedl na začátku, jedno z nejužitečnějších použití je číslování nadpisů, kde přiřadíte styly jednotlivým elementům H1, H2, atd.
Co když ale potřebujete číslovat prvky, které nemají takhle oddělenou strukturu a je potřeba použít pro všechny stejný tag (např. LI, DIV, A, apod.)? Určitě vás napadne udělat si CSS třídy, které budete prvkům přiřazovat:
.level-0 { counter-reset: level-1; }
.level-1:before {
counter-reset: level-2;
counter-increment: level-1;
content: counter(level-1);
}
.level-2:before {
counter-reset: level-3;
counter-increment: level-2;
content:
counter(level-1) '.' counter(level-2);
}
/* atd. */
Tím ale trochu popíráte onu automatičnost počítání a navíc se zbavujete možnosti nekonečného vnořování (vždy jste omezeni počtem úrovní, které napíšete do stylu).
CSS k tomu má samozřejmě nástroj, který funguje automaticky. Když totiž nastavíte vlastnost counter-reset na nějaký prvek, který se může vnořovat sám do sebe (např. <div><div></div></div>), prohlížeč při vnoření automaticky vytvoří nový counter (se stejným jménem), který bude používat ve vnořené úrovni (a s uzavíracím tagem daného prvku ho zase smaže a vrátí se k předchozímu):
div { counter-reset: spans; }
span { display:block; counter-increment: spans; }
span:before { content: counter(spans) '. '; }
div div span { padding-left: 2em; }
<div>
<span><span>
<div><span><span></div>
<span>
</div>
Vygeneruje:
1.
2.
1.
2.
3.
„To je šikovné, ale jak vypsat čísla všech úrovní, které jsme používali u nadpisů“, říkáte si? K tomu slouží funkce counters() (všimněte si „S“ na konci), která funguje jako funkce join() nebo implode() z JS či PHP a automaticky spojí všechny úrovně daného counteru:
div { counter-reset: spans; }
span { display:block; counter-increment: spans; }
span:before { content: counters(spans,'.') ' '; }
/* tady je ten rozdíl ------^ a ---^ */
div div span { padding-left: 2em; }
<div>
<span><span>
<div><span><span></div>
<span>
</div>
Vygeneruje:
1.
2.
2.1
2.2
3.
I funkce counters() může používat formát a to tak, že ho uvedete jako třetí parametr:
span:before {
content: counters(spans, '-', upper-latin);
}
Vygeneruje např.:
...
C-X
C-X-A
C-X-B
Počítat či nepočítat
Jak jsem již psal, counter se zvyšuje vždy při vykreslení prvku, což znamená, že pokud se prvek nevykreslí, nezvýší ani counter:
.get-1-point { counter-increment: points; }
.get-1-point.disabled { display: none; }
/* disabled prvky nebudou zvyšovat skóre */
Naproti tomu i neviditelné prvky se stále musí „vykreslovat“ (prohlížeč musí zjistit, jak budou velké, aby pro ně rezervoval místo), takže použití vlastnosti visibility:hidden nebo opacity:0 zajistí započtení prvku (pomocí position:absolute můžete zajistit, aby takové prvky nerozbíjely strukturu stránky):
.get-1-point.hidden {
opacity: 0;
} /* není vidět, ale stále s ním lze pracovat */
.bonus-points {
counter-increment: points 50;
visibility: hidden;
position: absolute;
} /* není ve stránce, ale stále se počítá */
Počítat, ale kolikrát?
Při vykreslování se počítá i s fiktivními prvky :before a :after. Pokud tedy nadefinujete counter-increment jak do prvku tak i jeho :before a/nebo :after, bude započítán vícekrát. Použít se to dá i k různým způsobům, jak prvek započít:
body { counter-reset: odrazky 0; }
.odrazka:before {
counter-increment: odrazky;
content: counter(odrazky);
}
/* tento zápis je ekvivalentní: */
body { counter-reset: odrazky 1; }
.odrazka:before { content: counter(odrazky); }
.odrazka { counter-increment: odrazky; }
A použít se to dá i k tomu, když má prvek zvýšit různé countery a nechcete řešit problém s přepisováním pravidel:
/* prvek .get.add-1-point zvýší pouze points,
ale ne collected -> problém */
.get { counter-increment: collected; }
.get.add-1-point { counter-increment: points; }
/* Tohle už bude fungovat správně: */
.get:after { counter-increment: collected; }
.get.add-1-points { counter-increment: points 1 }
.get.add-2-points { counter-increment: points 2 }
Pozor na to, že prvky se generují v pořadí: hlavní prvek, :before
a :after
. Pokud tedy plánujete zobrazit counter
() v :before
a pak ho inkrementovat v hlavním prvku, je potřeba myslet na to, že číslo se ve skutečnosti zvětší ještě přes tím, než se vykreslí! (Je tedy potřeba správně zvolit výchozí hodnotu 0 místo logicky očekávané 1 a nebo přesunout inkrement do :after
.)
Obdobně, pokud counter
zvětšíte v :after
, bude hodnota v :before
o 1 menší než ta v :after
, protože se :before
vykreslí před zvětšením a :after
se vykreslí až po zvětšení.
Další zdroje: