Wykazy - CSS
Automatyczna numeracja wykazu {counter-reset, counter-increment}
W jaki sposób stworzyć automatyczną numerację punktów i podpunktów w stylu: 1, 1.1, 1.1.1 itd.?
Automatyczna numeracja wykazu {counter-reset, counter-increment}
Jeżeli zaznajomiłeś się już czytelniku bardziej szczegółowo z zagadnieniem wykazów w języku HTML, to zapewne wiesz już, że nie oferują one żadnych bardziej skomplikowanych sposobów numeracji poszczególnych punktów i podpunktów. To znaczy możliwe jest określenie np. następującego sposobu numeracji:
- Punkty główne - liczby rzymskie: I, II, III,...
- Podpunkty - liczby arabskie: 1, 2, 3,...
- Podpunkty drugiego rzędu - litery: a, b, c,...
Co jednak zrobić, jeżeli nie odpowiada nam taki sposób numeracji punktów albo nie możemy z góry określić ile będziemy mieli poziomów zagnieżdżenia? CSS daje na to prostą receptę - automatyczną numerację, czyli możliwość dowolnego określania sposobu numerowania elementów, np.:
- Punkty główne: 1., 2., 3.,...
- Podpunkty: 1.1., 1.2., 1.3.,... 2.1., 2.2., 2.3.,... 3.1., 3.2., 3.3.,...
- Podpunkty drugiego rzędu: 1.1.1., 1.1.2., 1.1.3.,... 1.2.1., 1.2.2., 1.2.3.,... 2.1.1., 2.1.2., 2.1.3.,... 2.2.1., 2.2.2., 2.2.3.,...
- i tak dalej...
Automatyczna numeracja w CSS korzysta z tzw. liczników, które są podobne do zmiennych w językach programowania. Każdy licznik posiada swój unikalny identyfikator (nazwę), za pomocą którego można się do niego odnosić w arkuszu CSS. Dodatkowo każdy licznik przechowuje wartość, która jest liczbą określającą wartość licznika dla aktualnego punktu wykazu (1, 2, 3,...).
Zasada tworzenia automatycznej numeracji jest prosta:
- Najpierw określany nazwę licznika dla danego wykazu, od razu zerując go:
ol { counter-reset: nazwa_licznika }
- Usuwamy domyślne numerowanie wykazu, aby nie kolidowało z automatycznym:
ol li { list-style-type: none }
- Następnie dla kolejnych punktów i podpunktów wykazu wyświetlamy aktualną wartość licznika na początku treści, a następnie zwiększamy tą wartość o jeden - dla następnego punktu lub podpunktu:
ol li:before { content: counters(nazwa_licznika, ".") ". "; counter-increment: nazwa_licznika }
Oto efekt:
- Punkt pierwszy (1.)
- Podpunkt pierwszy (1.1.)
- Podpunkt drugiego rzędu (1.1.1.)
- Podpunkt drugiego rzędu (1.1.2.)
- Podpunkt drugi (1.2.)
- Podpunkt drugiego rzędu (1.2.1.)
- Podpunkt drugiego rzędu (1.2.2.)
- Podpunkt pierwszy (1.1.)
- Punkt drugi (2.)
- Podpunkt pierwszy (2.1.)
- Podpunkt drugiego rzędu (2.1.1.)
- Podpunkt drugiego rzędu (2.1.2.)
- Podpunkt drugi (2.2.)
- Podpunkt drugiego rzędu (2.2.1.)
- Podpunkt drugiego rzędu (2.2.2.)
- Podpunkt pierwszy (2.1.)
Ponieważ przedstawiony powyżej wykaz jest wielokrotnie zagnieżdżony, więc znajduje się w nim kilka znaczników ol
umieszczonych jeden wewnątrz drugiego. Dlatego właśnie polecenie ol { counter-reset: nazwa_licznika }
tak naprawdę przy każdym znaczniku ol
tworzy nowy egzemplarz tego licznika. Egzemplarze można oznaczać dla potrzeb analizy kolejnymi numerami (przyjmijmy numerację od zera): nazwa_licznika[0], nazwa_licznika[1], nazwa_licznika[2] itd. Każdy z egzemplarzy przechowuje osobną numerację (działają niezależnie od siebie), ale jednocześnie dla każdego z egzemplarzy tego samego licznika (czyli egzemplarzy z tą samą nazwą) można uzyskać aktualny stan wszystkich liczników nadrzędnych innych egzemplarzy. Realizuje się to za pomocą funkcji counters()
, która przyjmuje dwa argumenty rozdzielone przecinkiem:
- identyfikator licznika (wspólna nazwa egzemplarzy),
- łańcuch znakowy separatora, który ma rozdzielać kolejne wartości egzemplarzy licznika.
Wartość odczytaną przez tę funkcję możemy następnie podstawić w deklaracji stylu do własności content
, co spowoduje wyświetlenie jej w treści elementu:
ol li:before { content: counters(nazwa_licznika, ".") }
W świetle tego przebieg generowania automatycznej numeracji dla przedstawionego wcześniej wykazu odbywa się następująco:
<ol> <!-- nazwa_licznika[0] = 0 --> <li>1 (nazwa_licznika[0] + 1 = 1) <ol> <!-- nazwa_licznika[1] = 0 --> <li>1.1 (nazwa_licznika[1] + 1 = 1) <ol> <!-- nazwa_licznika[2] = 0 --> <li>1.1.1 (nazwa_licznika[2] + 1 = 1)</li> <li>1.1.2 (nazwa_licznika[2] + 1 = 2)</li> </ol> </li> <li>1.2 (nazwa_licznika[1] + 1 = 2) <ol> <!-- nazwa_licznika[3] = 0 --> <li>1.2.1 (nazwa_licznika[3] + 1 = 1)</li> <li>1.2.2 (nazwa_licznika[3] + 1 = 2)</li> </ol> </li> </ol> </li> <li>2 (nazwa_licznika[0] + 1 = 2) <ol> <!-- nazwa_licznika[4] = 0 --> <li>2.1 (nazwa_licznika[4] + 1 = 1) <ol> <!-- nazwa_licznika[5] = 0 --> <li>2.1.1 (nazwa_licznika[5] + 1 = 1)</li> <li>2.1.2 (nazwa_licznika[5] + 1 = 2)</li> </ol> </li> <li>2.2 (nazwa_licznika[4] + 1 = 2) <ol> <!-- nazwa_licznika[6] = 0 --> <li>2.2.1 (nazwa_licznika[6] + 1 = 1)</li> <li>2.2.2 (nazwa_licznika[6] + 1 = 2)</li> </ol> </li> </ol> </li> </ol>
Oprócz funkcji counters()
istnieje również funkcja counter()
, która działa bardzo podobnie, jednak przyjmuje jako argument jedynie nazwę identyfikatora licznika, a odczytuje tylko wartość aktualnego egzemplarza licznika, czyli pojedynczą liczbę. Użycie w powyższym przykładzie funkcji counter()
zamiast counters()
, spowodowałoby wyświetlenie wykazu numerowanego tradycyjnie:
- Punkt pierwszy (1.)
- Podpunkt pierwszy (1.)
- Podpunkt drugiego rzędu (1.)
- Podpunkt drugiego rzędu (2.)
- Podpunkt drugi (2.)
- Podpunkt drugiego rzędu (1.)
- Podpunkt drugiego rzędu (2.)
- Podpunkt pierwszy (1.)
- Punkt drugi (2.)
- Podpunkt pierwszy (1.)
- Podpunkt drugiego rzędu (1.)
- Podpunkt drugiego rzędu (2.)
- Podpunkt drugi (2.)
- Podpunkt drugiego rzędu (1.)
- Podpunkt drugiego rzędu (2.)
- Podpunkt pierwszy (1.)
Jeżeli w jednej deklaracji chcemy ustawić lub zwiększyć więcej niż jeden licznik, nie należy wstawiać kilka razy tej samej własności counter-reset
czy counter-increment
, lecz umieścić kilka identyfikatorów liczników w tej samej wartości, rozdzielonych spacjami:
ol { counter-reset: licznik1 licznik2 } ol li { counter-increment: licznik1 licznik2 }
Ponadto warto odnotować, że counter-reset
wcale nie musi ustawiać licznika na zero, a counter-increment
wcale nie musi zwiększać licznika tylko o jeden. Licznik może być nawet zmniejszany, poprzez podanie wartości ujemnej (numeracja w tył):
ol { counter-reset: licznik 3 } ol li { list-style-type: none } ol li:before { content: counters(licznik, ".") ". "; counter-increment: licznik -1 }
- Punkt drugi (2.)
- Podpunkt drugi (2.2.)
- Podpunkt drugiego rzędu (2.2.2.)
- Podpunkt drugiego rzędu (2.2.1.)
- Podpunkt pierwszy (2.1.)
- Podpunkt drugiego rzędu (2.1.2.)
- Podpunkt drugiego rzędu (2.1.1.)
- Podpunkt drugi (2.2.)
- Punkt pierwszy (1.)
- Podpunkt drugi (1.2.)
- Podpunkt drugiego rzędu (1.2.2.)
- Podpunkt drugiego rzędu (1.2.1.)
- Podpunkt pierwszy (1.1.)
- Podpunkt drugiego rzędu (1.1.2.)
- Podpunkt drugiego rzędu (1.1.1.)
- Podpunkt drugi (1.2.)
Zatem ogólna postać wartości counter-reset
i counter-increment
jest następująca: lista identyfikatorów liczników rozdzielonych spacjami, przy których po każdym z nich może opcjonalnie wystąpić liczba oddzielona od swojego (poprzedzającego) identyfikatora licznika również spacją:
ol { counter-reset: licznik1 licznik2 3 } ol li { counter-increment: licznik1 licznik2 -1 }
Automatyczną numerację można wykorzystać nie tylko do wykazów, ale również np. do tytułów:
body { counter-reset: h1 } h1 { counter-reset: h2 } h1:before { content: counter(h1) ". "; counter-increment: h1 } h2 { counter-reset: h3 } h2:before { content: counter(h1) "." counter(h2) ". "; counter-increment: h2 } h3 { counter-reset: h4 } h3:before { content: counter(h1) "." counter(h2) "." counter(h3) ". "; counter-increment: h3 } h4 { counter-reset: h5 } h4:before { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) ". "; counter-increment: h4 } h5 { counter-reset: h6 } h5:before { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) ". "; counter-increment: h5 } h6:before { content: counter(h1) "." counter(h2) "." counter(h3) "." counter(h4) "." counter(h5) "." counter(h6) ". "; counter-increment: h6 }
Powyższe reguły stylów będą funkcjonowały prawidłowo tylko w przypadku poprawnego semantycznie kodu, tzn. każdy element h2
powinien być poprzedzony przez h1
, h3
- przez h2
, h4
- przez h3
, h5
- przez h4
, h6
- przez h5
.
Style licznika
Domyślnie liczniki automatycznej numeracji mają postać liczb arabskich. Możliwe jest jednak przypisanie dowolnego innego stylu stosowanego dla wykazów:
ol { counter-reset: upper_alpha } ol li { list-style-type: none } ol li:before { content: counters(upper_alpha, ".", upper-alpha) ") "; counter-increment: upper_alpha }
- Punkt pierwszy (A)
- Podpunkt pierwszy (A.A)
- Podpunkt drugiego rzędu (A.A.A)
- Podpunkt drugiego rzędu (A.A.B)
- Podpunkt drugi (A.B)
- Podpunkt drugiego rzędu (A.B.A)
- Podpunkt drugiego rzędu (A.B.B)
- Podpunkt pierwszy (A.A)
- Punkt drugi (B)
- Podpunkt pierwszy (B.A)
- Podpunkt drugiego rzędu (B.A.A)
- Podpunkt drugiego rzędu (B.A.B)
- Podpunkt drugi (B.B)
- Podpunkt drugiego rzędu (B.B.A)
- Podpunkt drugiego rzędu (B.B.B)
- Podpunkt pierwszy (B.A)
Numeracja typu: I.i.1.A.a.α., I.i.1.a.β.,... I.i.1.A.b.α., I.i.1.A.b.β.,... I.i.1.B.b.α., I.i.1.B.b.β.,... I.i.2.B.b.α., I.i.2.B.b.β.,... I.ii.2.B.b.α., I.ii.2.B.b.β.,... II.ii.2.B.b.α., II.ii.2.B.b.β.,...
body { counter-reset: h1 } h1 { counter-reset: h2 } h1:before { content: counter(h1, upper-roman) ". "; counter-increment: h1 } h2 { counter-reset: h3 } h2:before { content: counter(h1, upper-roman) "." counter(h2, lower-roman) ". "; counter-increment: h2 } h3 { counter-reset: h4 } h3:before { content: counter(h1, upper-roman) "." counter(h2, lower-roman) "." counter(h3, decimal) ". "; counter-increment: h3 } h4 { counter-reset: h5 } h4:before { content: counter(h1, upper-roman) "." counter(h2, lower-roman) "." counter(h3, decimal) "." counter(h4, upper-alpha) ". "; counter-increment: h4 } h5 { counter-reset: h6 } h5:before { content: counter(h1, upper-roman) "." counter(h2, lower-roman) "." counter(h3, decimal) "." counter(h4, upper-alpha) "." counter(h5, lower-alpha) ". "; counter-increment: h5 } h6:before { content: counter(h1, upper-roman) "." counter(h2, lower-roman) "." counter(h3, decimal) "." counter(h4, upper-alpha) "." counter(h5, lower-alpha) "." counter(h6, lower-greek) ". "; counter-increment: h6 }
Firefox, Chrome i Konqueror obsługują również style typu: disc (koło), circle (okrąg) i square (kwadrat).