Dynamiczne filtrowanie “tabeli”

5 Comments

Chyba za krótko zastanawiałem się nad tytułem tego posta… no cóż.
Stanąłem ostatnio przed koniecznością stworzenia tabeli produktów z filtrowaniem dynamicznym. Ładnie brzmi, prawda? A oto, co mam na myśli:
– każdy produkt posiada swoje “tagi”, według których wyróżnione są jego funkcje/wygląd/zastosowanie
– każdy produkt może mieć dowolną ilość tagów
– użytkownik może zaznaczyć, które produkty chce widzieć. Nie chcemy przy tym przeładowywać strony.

Chwila zastanowienia… jQuery. Oczywiście :)

Zacznijmy od szkieletu strony. Dla potrzeb automatycznego układania produktów (nie chce mi się nazywać ich “obiektami”, ale tak należy o nich myśleć, aby nie ograniczać rozważań do zastosowań komercyjno-handlowych) stworzymy następujący szkielet strony:

  <body>
      <div id="searcher">
        Wyszukiwanie:
      </div>
 
      <div id="maindiv">
        <div class="product okragle">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
        <div class="product okragle">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
        <div class="product prostokatne">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
        <div class="product trojkatne">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
        <div class="product okragle prostokatne trojkatne">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
        <div class="product szesciokatne">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
        <div class="product szesciokatne prostokatne">
          <img src="img/dummy.jpg"><br>
          Nazwa produktu
        </div>
      </div>
  </body>

Nie będę Was tu zanudzał deklaracjami doctype i podobnymi, w chwili obecnej nie są ważne.

Co tu stworzyliśmy: jest to kilka DIVów, które chcemy ułożyć na kształt tabeli oraz jeden DIV na górze, mający nam służyć za wyszukiwarkę.
Styl dla naszych produktów należy przypisać adekwatny do zadania:

.product
{
  width: 175px;
  height: 180px;
  margin: 6px;
  padding: 4px;
  text-align: center;
  display: block;
  float: left;
  border-style: solid;
  border-width: 1px;
}

Najważniejszy jest oczywiście atrybut float. Spowoduje on, że po odznaczeniu konkretnego tagu, gdy produkt o tym tagu będzie “zanikał”, reszta będzie “wskakiwała” na jego miejsce.

Idźmy dalej. Należy zauważyć, że każdy produkt ma przypisanych kilka klas. Główna, czyli product jest identyfikatorem obiektu, zaś reszta to tagi (w tym przykładzie odnoszą się do kształtu).

Przychodzi czas na stworzenie prostej wyszukiwarki (bardziej narzędzia filtrującego, ale “wyszukiwarka” lepiej brzmi). W tym celu zbieramy wszystkie tagi (klasy) z obiektów klasy product, grupujemy, sortujemy i wyświetlamy z checkbox’ami. Na początek tworzymy metodę usuwającą duplikaty z obiektu Array JavaScript

UWAGA! Metoda unique() z jQuery nie zadziała dla tablic zawierających “zwykłe” elementy (znaki, łańcuchy, liczby etc.). Działa tylko dla tablic klasycznych obiektów, czyli innych niż te opisywane jako “value-types”. Więcej na ten temat TUTAJ.

A oto funkcja usuwająca duplikaty z tablicy (zaczerpnięta gdzieś z czeluści Sieci):

Array.prototype.unique = function () {
  var r = new Array();
  o:for(var i = 0, n = this.length; i < n; i++)
  {
    for(var x = 0, y = r.length; x < y; x++)
      if(r[x]==this[i])
        continue o;
  r[r.length] = this[i];
  }
  return r;
}

Teraz nadchodzi czas na właściwą część imprezy. Resztę kodu umieszczamy w obsłudze zdarzenia onLoad dokumentu, czyli dla jQuery w argumencie funkcji ready():

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

Na początek, w celu podglądu, wewnątrz każdego produktu (obiektu klasy product) utworzymy listę przypisanych mu klas (tagów). Przy okazji stworzymy ogólną listę wszystkich tagów zebranych ze wszystkich produktów:

// lista wszystkich tagow:
var tagi = Array();
 
// lista klas kazdego produktu:
$(".product").each(function()
{
  // pobieramy atrybut "class" i rozbijamy na tablice
  var classes = $(this).attr("class").split(" ");
 
  // wszystkie klasy oprocz "product" umieszczamy w tablicy "tagi"
  for(a = 0; a < classes.length; a++)
    if(classes[a] != 'product') // && !in_array(present, tagi))
      tagi.push(classes[a]);
 
  // usuwamy klase "product" z poczatku stringa (substr(9)), następnie łączymy
  // klasy (join()) umieszczając pomiędzy nimi przecinek i spacje
  $(this).append('<div class="klasy">' + classes.join(', ').substr(9) + '</div>');
});
 
// usuwamy duplikaty tagow
tagi = tagi.unique();

Następnie musimy utworzyć prosty interfejs wyszukiwarki (dokładniej: narzędzia filtrowania, ale “wyszukiwarka” brzmi bardziej przystępnie). W tym celu do DIVa "searcher" dodajemy listę pól typu checkbox, każdy odpowiadający za jeden tag. Oczywiście na samym początku zaznaczamy wszystkie checkbox’y i wyświetlamy wszystkie produkty:

// najpierw "opakowanie"
$("#searcher").append('<div id="tags">');
 
// iterujemy tablicę tagów
for(a = 0; a < tagi.length; a++)
  $("#searcher").append('<span class="tag_input"><input type="checkbox" class="tag_checkbox" id="' + 
  tagi[a] + 
  '" checked><label class="tag_label" for="' + tagi[a] + '">' + tagi[a] + '</label></span>');
 
// zamykamy "opakowanie"
$("#searcher").append('</div>');

Jak widać, każdy checkbox ma ID równe swojemu tagowi oraz klasę "tag_checkbox", która przysłuży nam się już za chwilę.

Teraz moment zastanowienia nad funkcjonalnością naszego kodu. Co chcemy uzyskać?

Jeśli produkt ma jeden tag, po jego odznaczeniu w wyszukiwarce, produkt ma “znikać” z listy, zaś po zaznaczeniu – pojawiać się. Co, jeśli ma więcej tagów? Logicznym rozwiązaniem będzie postanowienie, że pojawić się (ogólnie być widocznym) powinien produkt, którego przynajmniej jeden z tagów jest w wyszukiwarce zaznaczony, zaś zniknąć z listy produkt może dopiero wtedy, kiedy odznaczone w wyszukiwarce będą wszystkie jego tagi. Dlatego nie wystarczy sprawdzić, w momencie zmiany wartości checkbox’a czy jest on odznaczony i schować produktu – trzeba też sprawdzić, czy checkbox’y odpowiadające wszystkim pozostałym tagom danego produktu są odznaczone. Wtedy produkt można ukryć:

$(".tag_checkbox").change(function()
{
  // jesli dowolny tag tego produktu jest zaznaczony,
  // pokaz ten produkt
  if($(this).attr("checked"))
    $("." + $(this).attr("id")).fadeIn();
  // w przeciwnym wypadku najpierw sprawdz 
  // wszystkie inne tagi produktu:
  else
    $("." + $(this).attr("id")).each(function()
    {
      // ekstrakcja klas (tagow) produktu:
      var myClasses = $(this).attr("class").split(" ");
 
      // flaga sprawdzajaca, czy ktorakolwiek z klas 
      // danego produktu jest zaznaczona:
      var is_checked = false;
 
      // i sprawdzenie, czy wszystkie klasy produktu sa odznaczone:
      for(a = 0; a < myClasses.length; a++)
        if($("#" + myClasses[a]).attr("checked"))
          // jesli ktoras z klas jest zaznaczona, 
          // nie mozemy ukryc produktu
          is_checked = true;
      // wszystkie klasy produktu sa odznaczone, ukrywamy
      if(!is_checked)
        $(this).fadeOut();
    });
});

Ten oto prostu kod pozwala na stworzenie imitacji tabeli z dynamicznym filtrowaniem obiektów (komórek). Działający (mam nadzieję) przykład, znajdziecie TUTAJ.

UPDATE:
W odpowiedzi na pytanie wiluxx‘a: zmiany są bardzo niewielkie. Po pierwsze w pliku stylu, przy opisie klasy .product należy zmienić atrybut display na none. Po drugie, w skrypcie przy tworzeniu checkbox’ów (z miejscu iterowania tablicy tagów) należy opuścic atrybut checked.
Taki przykład, znajduje się TUTAJ.

Categories: JavaScript, jQuery, Zabawy z kodem Tags: Tagi:

5 thoughts on “Dynamiczne filtrowanie “tabeli””

  1. Dobre. Konkretne i z żywym przykładem co najważniejsze. Produkty trochę zbyt agresywnie ‘skaczą’ przy zajmowaniu zwolnionych miejsc, ale nie wiem co z tym można by zrobić…

  2. Interesujący opis. A jak należałoby to przerobić, tak żeby dopiero po zaznaczeniu checkbox wyświetlały się obiekty?

  3. Bardzo pomocny artykuł. Mam jednak problem z odnalezieniem kodu odpowiedzialnego za wypisywanie samych tagow. Chodzi o to zeby po zaznaczeniu checkboxa byla wyswietlana sama zawartosc np. nazwa produktu i zdj, ale bez nazwy kategorii (okragle itp)… bylabym super wdzieczna za pomoc :-)
    pozdrówki,
    alakoala

  4. już sobie poradziłam,
    chodziło o fragment:
    ‘ + classes.join(‘, ‘).substr(9) + ‘

    pozdrawiam,
    alakoala

Leave a Reply

Your email address will not be published. Required fields are marked *