Przejdź do treści

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}

Chrome Firefox Edge Opera Safari

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:

  1. Najpierw określany nazwę licznika dla danego wykazu, od razu zerując go:
    ol { counter-reset: nazwa_licznika }
  2. Usuwamy domyślne numerowanie wykazu, aby nie kolidowało z automatycznym:
    ol li { list-style-type: none }
  3. 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:

  1. Punkt pierwszy (1.)
    1. Podpunkt pierwszy (1.1.)
      1. Podpunkt drugiego rzędu (1.1.1.)
      2. Podpunkt drugiego rzędu (1.1.2.)
    2. Podpunkt drugi (1.2.)
      1. Podpunkt drugiego rzędu (1.2.1.)
      2. Podpunkt drugiego rzędu (1.2.2.)
  2. Punkt drugi (2.)
    1. Podpunkt pierwszy (2.1.)
      1. Podpunkt drugiego rzędu (2.1.1.)
      2. Podpunkt drugiego rzędu (2.1.2.)
    2. Podpunkt drugi (2.2.)
      1. Podpunkt drugiego rzędu (2.2.1.)
      2. Podpunkt drugiego rzędu (2.2.2.)

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:

  1. identyfikator licznika (wspólna nazwa egzemplarzy),
  2. ł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:

  1. Punkt pierwszy (1.)
    1. Podpunkt pierwszy (1.)
      1. Podpunkt drugiego rzędu (1.)
      2. Podpunkt drugiego rzędu (2.)
    2. Podpunkt drugi (2.)
      1. Podpunkt drugiego rzędu (1.)
      2. Podpunkt drugiego rzędu (2.)
  2. Punkt drugi (2.)
    1. Podpunkt pierwszy (1.)
      1. Podpunkt drugiego rzędu (1.)
      2. Podpunkt drugiego rzędu (2.)
    2. Podpunkt drugi (2.)
      1. Podpunkt drugiego rzędu (1.)
      2. Podpunkt drugiego rzędu (2.)

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 }
  1. Punkt drugi (2.)
    1. Podpunkt drugi (2.2.)
      1. Podpunkt drugiego rzędu (2.2.2.)
      2. Podpunkt drugiego rzędu (2.2.1.)
    2. Podpunkt pierwszy (2.1.)
      1. Podpunkt drugiego rzędu (2.1.2.)
      2. Podpunkt drugiego rzędu (2.1.1.)
  2. Punkt pierwszy (1.)
    1. Podpunkt drugi (1.2.)
      1. Podpunkt drugiego rzędu (1.2.2.)
      2. Podpunkt drugiego rzędu (1.2.1.)
    2. Podpunkt pierwszy (1.1.)
      1. Podpunkt drugiego rzędu (1.1.2.)
      2. Podpunkt drugiego rzędu (1.1.1.)

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 }
  1. Punkt pierwszy (A)
    1. Podpunkt pierwszy (A.A)
      1. Podpunkt drugiego rzędu (A.A.A)
      2. Podpunkt drugiego rzędu (A.A.B)
    2. Podpunkt drugi (A.B)
      1. Podpunkt drugiego rzędu (A.B.A)
      2. Podpunkt drugiego rzędu (A.B.B)
  2. Punkt drugi (B)
    1. Podpunkt pierwszy (B.A)
      1. Podpunkt drugiego rzędu (B.A.A)
      2. Podpunkt drugiego rzędu (B.A.B)
    2. Podpunkt drugi (B.B)
      1. Podpunkt drugiego rzędu (B.B.A)
      2. Podpunkt drugiego rzędu (B.B.B)

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).

Komentarze

Zobacz więcej komentarzy

Facebook