Wielostanowy checkbox w jQuery

Witam. Dziś przy okazji pisania kolejnego rozszerzenia MediaWiki (więcej o tym już niedługo), natknąłem się na konieczność utworzenia 3-stanowego checkbox’a. Pomyślałem sobie: dlaczego ograniczać się do trzech stanów? Dlatego w tym wpisie przedstawię przepis na – ogólnie rzecz ujmując – checkbox wielostanowy.

W całości będzie nam pomagać jQuery.

Nie jest moim zamiarem prowadzenie tu lekcji podstaw jQuery, gdyż robiono to już wiele razy w różnych miejscach. Jednymi z najlepszych artykułów na początek, jakie znalazłem, są teksty Ferrante.

Zacznijmy od założeń wstępnych. Załóżmy, że mamy listę użytkowników i każdemu potrzebujemy przypisać uprawnienia. Będą to:

  • odczyt
  • odczyt i zapis
  • brak uprawnień

Jak widać, są to trzy stany. Oznaczmy je kolejno jako “R”, “RW” oraz ” “. Chcemy, aby klikanie w poszczególnych użytkowników zmieniało kolejno te stany. Najpierw stwórzmy przykładową listę użytkowników:

<label class="MyLabel"><span class="CN">[<span class="MyEntry"></span>]</span> użytkownik 1</label><br>
<label class="MyLabel"><span class="CN">[<span class="MyEntry"></span>]</span> użytkownik 2</label><br>
<label class="MyLabel"><span class="CN">[<span class="MyEntry"></span>]</span> użytkownik 3</label><br>

Jak widać, w każdej linii obiektem nadrzędnym jest LABEL. Bo tak. Może spodobało mi się, że po najechaniu nań myszą, kursor nie zmienia się w pionową kreseczkę :)
Elementem podrzędnym jest obiekt SPAN klasy CN oraz nazwa użytkownika. Skąd “CN”? Od “Courier New” – lepiej wygląda takie pole utworzone czcionką o stałej szerokości znaków:

.CN
{
  font-family: Courier New;
}

Pole to zawiera nawias kwadratowy, wewnątrz którego znajduje się kolejny obiekt SPAN, klasy MyEntry. Obiekt ten będzie zawierał symbol uprawnienia nadanego użytkownikowi.

Jak to ma działać? Klikamy dowolne miejsce danej linii, a wewnątrz nawiasów kwadratowych pojawia się kolejne oznaczenie.
Teraz musimy stworzyć tablicę oznaczeń:

var Entries = new Array(
  '&nbsp;&nbsp;',
  'R&nbsp;',
  'RW'
);

Następnie należy zainicjalizować wszystkie pola jakąś wartością. W tym celu stworzymy funkcję, która zrobi to za nas:

function InitBoxes(Entry)
{
  // sprawdzenie, czy podano poprawny indeks tablicy
  if(isNaN(Entry) || Entry < 0 || Entry > (Entries.length -1))
     Entry = 0;
 
  $(".MyEntry").html(Entries[Entry]);
}

Jak widać, funkcja nadpisuje zawartość wszystkich obiektów klasy MyEntry zawartością elementu tablicy Entries o podanym (lub zerowym) indeksie. Na początku oczywiście sprawdzana jest poprawność otrzymanego przez funkcję argumentu.

Teraz kolej na jQuery i przejęcie zdarzenia załadowania dokumentu, czyli całą resztę kodu JS umieszczamy w argumencie funkcji ready():

$().ready(function()
{
  // tutaj kod...
});

Na początek inicjalizacja pól:

InitBoxes();

JavaScript nie zgłosi zastrzeżeń w przypadku braku argumentu, a obsługę takiego przypadku zawarliśmy wcześniej w funkcji InitBoxes(), sprawdzając, czy podany argument funkcji jest “nie-liczbą”, czyli isNaN(Entry).

Następnie zdarzeniu onClick każdego obiektu klasy MyLabel przypisujemy odpowiednią funkcję:

$(".MyLabel").click(function()
{
  // skrot do obiektu:
  var CurrentBox   = $("span[@class=MyEntry]", this);
  // obecna zawartosc:
  var CurrentEntry = CurrentBox.html();
  // pusty indeks tablicy:
  var CurrentIndex = null;
 
  // przeszukiwanie tablicy w celu znalezienia indeksu
  // obecnej zawartości:
  for(A = 0; A < Entries.length; A++)
  {
    if(Entries[A] == CurrentEntry)
    {
      CurrentIndex = A;
      break;
    }
  }
 
  // jesli obecnie widoczny jest ostatni element tablicy,
  // nalezy uzyc pierwszego:
  if(CurrentIndex == (Entries.length - 1))
    CurrentBox.html(Entries[0]);
 
  else
    CurrentBox.html(Entries[CurrentIndex + 1]);
});

I oto cała filozofia. Konstrukcja ta pozwala na zastosowanie dowolnej ilości stanów – wystarczy dodać nowe elementy tablicy.
Jak odczytywać wartości z takich checkbox’ów? Najpierw należy jej jakoś wyróżnić, przykładowo przez unikatowe ID i odczytywać ich zawartość, czyli html().
Odczyt odbywałby się następująco:

$(".MyEntry").each(function()
{
  var uzytkownik  = $(this).attr("id");
  var uprawnienia = $(this).html();
  // tutaj obsluga pobranych danych
});

Działający przykład tego kodu można znaleźć TUTAJ.

Życzę powodzenia w wykorzystywaniu i przerabianiu kodu.