Kawałek Kodu

Zdarza się, że stworzyliśmy ekstra formularz ankiety i chcemy poinformować użytkownika korzystającego z niego o ilości zaznaczonych odpowiedzi czy też o ilości prawidłowych odpowiedzi. A to wszystko na bieżąco. Możemy to zrobić bez użycia JavaScript. Nie będzie trzeba dodawać obsługi zdarzeń, liczyć zaznaczonych, ustawiać w jakimś elemencie obliczonej wartości.

Wykorzystamy do tego CSS-owy licznik, a dokładnie zmienną, której wartością możemy sterować przy pomocy kombinacji HTML+CSS.

Zacznijmy od przykładu wykorzystania licznika do numerowania elementów listy. Co prawda można wykorzystać natywną numerację, ale ma on jednak mniejsze możliwości stylowania. Czyli na razie checkbox'y odkładamy na bok.

<ol>
  <li>Wstęp.</li>
  <li>Ubrania kiedyś i dziś.</li>
  <li>Rodzaje kreszu.</li>
  <li>Spódnice dla mężczyzn.</li>
  <li>Kapelusz nie tylko na głowę.</li>
  <li>Skórzane skarpety - czy warto?</li>
  <li>Zakończenie.</li>
</ol>

+

ol {
  list-style: none;
  padding: 0;
  counter-reset: licznik;
  font-size: 14px;
}

li {
  position: relative;
  padding-left: 25px;
  margin-bottom: 5px;
}

li:before {
  content: counter(licznik);
  position: absolute;
  left: 0;
  top: 50%;
  width: 16px;
  height: 16px;
  line-height: 16px;
  color: #fff;
  font-size: 10px;
  transform: translateY(-50%);
  background: #f80;
  border-radius: 50%;
  text-align: center;
  counter-increment: licznik;
}

ol li:nth-child(3n+2):before {
  background-color: #f44;
}

ol li:nth-child(3n+3):before {
  background-color: #08f;
}

To co nas tu mniej interesuje, to usunięcie numeracji listy oraz wykorzystanie pseudoelementu :before. To jemu przekazujemy wartość licznika, ale jednocześnie pozwala on na ustalenie innej wielkości czcionki oraz wielokolorowego wypunktowania listy.

A to co nas bardziej interesuje, to:

  • counter-reset - ustalamy tu nazwa_licznika oraz zerujemy jego wartość. Wartość resetujemy w elemencie OL (może to być też inny nadrzędny element, np. BODY). Domyślnie resetujemy na 0, ale możemy podać wartość startową za nazwa_licznika,
  • counter-increment - zwiększamy licznik o nazwie "licznik" o 1. Możem zwiększać lub zmniejszać o inną wartość, jeśli ją podamy za nazwa_licznika,
  • counter(nazwa_licznika) - funkcja zwracająca wartość licznika. Zwróconą wartość umieszczamy w elemencie :before.

Efekt jest taki:

Wróćmy teraz do checkbox'ów.

Przykład będzie mniej praktyczny, ale w prosty sposób będziesz mógł go wykorzystać do własnych, praktycznych celów.

<form>
 <fieldset>
  <legend>Reprezentacja bitowa bajtu</legend>
  <input type="checkbox" id="bit7" /><label for="bit7">7</label>
  <input type="checkbox" id="bit6" /><label for="bit6">6</label>
  <input type="checkbox" id="bit5" /><label for="bit5">5</label>
  <input type="checkbox" id="bit4" /><label for="bit4">4</label>
  <input type="checkbox" id="bit3" /><label for="bit3">3</label>
  <input type="checkbox" id="bit2" /><label for="bit2">2</label>
  <input type="checkbox" id="bit1" /><label for="bit1">1</label>
  <input type="checkbox" id="bit0" /><label for="bit0">0</label>
 </fieldset>
 <output name="bity">Bitów włączonych</output>
</form>

+

form {
  counter-reset: bity;
  overflow: hidden;
}

form input[type="checkbox"] {
  visibility: hidden;
  position: absolute;
  width: 0;
  height: 0;
}

form label {
  float: left;
  border: 1px solid #000;
  width: 20px;
  height: 20px;
  margin-right: -1px;
  text-align: center;
  line-height: 20px;
  cursor: pointer;
}

form input[type="checkbox"]:checked {
  counter-increment: bity;
}

form input[type="checkbox"]:checked+label {
  background: #000;
  color: #fff;
}

form output:after {
  content: ' ' counter(bity);
}

W powyższym przykładzie użyliśmy inkrementacji licznika dla selektora :checked, dzięki czemu licznik zwiększa swoją zawartość dla klikniętego checkbox'a. Odznaczenie checkbox'a powoduje zmniejszenie licznika - jest przeliczany na nowo. Niżej znajdziesz powód, dla którego nie można umieścić licznika w elemencie LEGEND (co by ładniej wyglądało). Ten kod możesz wykorzystać właśnie do wspomnianego na początku wpisu zliczania zaznaczonych odpowiedzi / opcji / produktów.

Zmodyfikujmy nieco przykład korzystając z możliwości modyfikacji licznika o dowolną wartość, a nie tylko o 1. Otrzymamy prosty konwerter bin2dec:

<form>
  <input type="checkbox" id="bit7" />
  <input type="checkbox" id="bit6" />
  <input type="checkbox" id="bit5" />
  <input type="checkbox" id="bit4" />
  <input type="checkbox" id="bit3" />
  <input type="checkbox" id="bit2" />
  <input type="checkbox" id="bit1" />
  <input type="checkbox" id="bit0" />
  <fieldset>
    <legend>wartość decymalna</legend>
    <label for="bit7">7</label>
    <label for="bit6">6</label>
    <label for="bit5">5</label>
    <label for="bit4">4</label>
    <label for="bit3">3</label>
    <label for="bit2">2</label>
    <label for="bit1">1</label>
    <label for="bit0">0</label>
  </fieldset>
</form>

+

form {
  counter-reset: bity;
  overflow: hidden;
}

form input[type="checkbox"] {
  visibility: hidden;
  position: absolute;
  width: 0;
  height: 0;
}

form label {
  float: left;
  border: 1px solid #000;
  width: 20px;
  height: 20px;
  margin-right: -1px;
  text-align: center;
  line-height: 20px;
  cursor: pointer;
}

form #bit0:checked {
  counter-increment: 1;
}

form #bit0:checked ~ fieldset label[for="bit0"],
form #bit1:checked ~ fieldset label[for="bit1"],
form #bit2:checked ~ fieldset label[for="bit2"],
form #bit3:checked ~ fieldset label[for="bit3"],
form #bit4:checked ~ fieldset label[for="bit4"],
form #bit5:checked ~ fieldset label[for="bit5"],
form #bit6:checked ~ fieldset label[for="bit6"],
form #bit7:checked ~ fieldset label[for="bit7"] {
  background: #000;
  color: #fff;
}
form #bit0:checked {
  counter-increment: bity 1;
}

form #bit1:checked {
  counter-increment: bity 2;
}

form #bit2:checked {
  counter-increment: bity 4;
}

form #bit3:checked {
  counter-increment: bity 8;
}

form #bit4:checked {
  counter-increment: bity 16;
}

form #bit5:checked {
  counter-increment: bity 32;
}

form #bit6:checked {
  counter-increment: bity 64;
}

form #bit7:checked {
  counter-increment: bity 128;
}

form legend:after {
  content: ': ' counter(bity) ;
}

Tu również zliczamy licznik dla :checked, ale dla każdego checkbox'a stworzyliśmy odrębną "funkcję" inkrementującą. Jako, że każdy checkbox reprezentuje bit na danej pozycji, to kiedy jest włączony to zwiększa licznik o 2^pozycja.

A gdzie ta obiecana ankieta?

Proszę bardzo:

<form>
  <fieldset>
    <legend>
      Ile to jest 2+2?
    </legend>
    <label for="o1t">4<input type="radio" class="tak" id="o1t" name="odpowiedz[1]" /></label>
    <label for="o1n">13<input type="radio" class="nie" id="o1n" name="odpowiedz[1]" /></label>
  </fieldset>
  <fieldset>
    <legend>
      Którą planetą od Słońca jest Ziemia?
    </legend>
    <label for="o2n1">2<input type="radio" class="nie" id="o2n1" name="odpowiedz[2]" /></label>
    <label for="o2n2">5<input type="radio" class="nie" id="o2n2" name="odpowiedz[2]" /></label>
    <label for="o2t">3<input type="radio" class="tak" id="o2t" name="odpowiedz[2]" /></label>
  </fieldset>
  <fieldset>
    <legend>
      Czy kawalekkodu.pl to fajny blog?
    </legend>
    <label for="o3t">tak<input type="radio" class="tak" id="o3t" name="odpowiedz[3]" /></label>
    <label for="o3n">nie<input type="radio" class="nie" id="o3n" name="odpowiedz[3]" /></label>
  </fieldset>
  <output name="odpowiedzi">Odpowiedzi</output>
</form>

+

form {
  counter-reset: prawidlowe bledne;
}

form input[type="radio"].tak:checked {
  counter-increment: prawidlowe;
}

form input[type="radio"].nie:checked {
  counter-increment: bledne;
}

form output:after {
  content: ' prawidłowe: ' counter(prawidlowe) ' / błedne: ' counter(bledne);
}

Korzystamy tu z dwóch liczników. Jeden będzie nam zliczał odpowiedzi prawidłowe (zaznaczone radio z klasą tak), drugi nieprawidłowe (zaznaczone radio z klasą nie). Oczywiście nie jest to przykład cwanoodporny, bo można zajrzeć w źródło kodu i sprawdzić, która odpowiedź jest prawidłowa.

Wskazówki:

  • licznik może być resetowany w elemencie na tym samym poziomie lub wyżej w drzewie DOM, ale zawsze przed inkrementacją i użyciem,
  • reset i inkrementacja nie działają na elementach z ustawionym stylem display:none,
  • funkcji counter() i counters() nie można użyć w elemencie wcześniejszym lub wyżej w drzewie DOM przed inkrementacją,
  • wartość licznika można umieścić tylko w pseudoelemencie :before i :after, a co za tym idzie nie jest ona widziana przez wszystkie screenreader'y.

Trzymam kciuki, aby powyższe przykłady natchnęły Cię do korzystania z liczników we własnych projektach. Bo na kogo można liczyć, jak nie na CSS-owy licznik!

 

Przydatne linki:
Dokumentacja counter-reset
Dokumentacja counter-increment
Przykłady używania licznika