Dobre praktyki - CSS
CSS Zorientowane Obiektowo
W jaki sposób tworzyć elastyczne strony WWW, które będą łatwe do rozwijania w przyszłości?
Wstęp
Każdy programista wie, jaki przełom w rozwoju informatyki przyniosła koncepcja Programowania Zorientowanego Obiektowo (ang. Object Oriented Programming). Dlaczego by nie przenieść tego na grunt CSS? Może zabrzmi to zaskakująco, ale CSS już teraz daje taką możliwość. Nie są wymagane do tego żadne dodatkowe rozszerzenia w przeglądarkach, a jedynie zmiana sposobu myślenia webmasterów podczas projektowania arkuszy stylów. Paradoksalnie osobom, które nigdy nie programowały w językach proceduralnych, prawdopodobnie łatwiej przyjdzie zrozumienie założeń CSS Zorientowanych Obiektowo (ang. Object Oriented CSS), gdyż są one bardzo intuicyjne.
Obiekty
Popatrz przez chwilę na dowolną stronę WWW (tak, możesz spojrzeć również na stronę, którą właśnie czytasz 🙂). Zwykle zawiera ona m.in.:
- nagłówek
- menu
- artykuł
- stopkę
Każdy z tych elementów z naszego punktu widzenia jest właśnie obiektem. Obiekt CSS to wizualnie samodzielny element strony. Artykuł potrafi wyświetlić się poprawnie bez nagłówka ani stopki serwisu. Taki sposób przedstawiania elementów strony jest dla człowieka bardziej intuicyjny niż pojmowanie jej jako zbiór wielu DIV-ów, SPAN-ów i innych znaczników. Obiektowość CSS sprowadza się do nadawania formatowania w kontekście takich obiektów, a nie poszczególnych znaczników występujących w kodzie źródłowym dokumentu. Pojedynczemu obiektowi zwykle odpowiada klasa CSS albo selektor identyfikatora:
#Header { /* nagłówek */ } .Menu { /* menu */ } .Article { /* artykuł */ } #Footer { /* stopka */ }
Warto zwrócić uwagę, że w przypadku stosowania systemów parsowania szablonów na serwerze (np. w języku PHP), każdy obiekt jest kandydatem na zapisanie jego znaczników w osobnym pliku szablonu.
Atrybuty
Często obiekty nie są na tyle proste, aby można było określić ich wygląd przy pomocy pojedynczej reguły stylów (jednego selektora). Menu nawigacyjne będzie zawierać elementy podrzędne - listę odnośników - którym również musimy przypisać określony styl. Atrybuty stanowią zbiór wszystkich elementów (znaczników) potomnych obiektu, którym jest przypisany styl.
<div class="Menu"> <ul> <li><a href="...">...</a></li> <li><a href="...">...</a></li> <li><a href="...">...</a></li> </ul> </div>
Powyżej przedstawiony obiekt Menu może składać się z następujących atrybutów:
- wykaz ul
- punkt wykazu li (jeśli chodzi o stylizację, nie ma znaczenia, ile punktów ma wykaz, ponieważ wygląd dla nich określamy wspólnie)
- odnośnik a
W arkuszu CSS pełna definicja tej klasy może wyglądać na przykład tak:
.Menu { background-color: white; } .Menu ul, .Menu li { display: block; list-style: none; margin: 0; padding: 0; } .Menu li { float: left; margin-right: 1em; } .Menu a { color: blue; text-decoration: none; }
Kompozycja
Zwróćmy uwagę, że typowy artykuł może zawierać informacje o:
- autorze - np. z jego imieniem, nazwiskiem i zdjęciem
- źródle - nazwa, logo i odnośnik
Każdy z tych elementów można traktować jako osobny obiekt (zmiana wyglądu informacji o autorze nie powinna wpływać na wygląd samej treści artykułu), z których składa się (ang. composition) główny obiekt artykułu. Myślenie o skomplikowanym obiekcie jako o złożeniu kilku mniejszych, samodzielnych obiektów, ułatwia nam pojmowanie otaczającej nas rzeczywistości. Patrząc na typowy komputer widzimy, że składa się on m.in. z ekranu, klawiatury, urządzenia wskazującego (np. myszki), a nie z masy połączonych ze sobą tranzystorów i innych elementów elektronicznych. Przeciętny człowiek nie byłby w stanie przyswoić tak wielkiej złożoności w postaci pojedynczego modelu. Z punktu widzenia kompozycji w obiektowości CSS chodzi o to, aby rozbijać w myślach skomplikowane obiekty na mniejsze i stylizować je oddzielnie. Korzyścią takiego podejścia będzie możliwość osadzenia np. informacji o autorze na osobnej stronie albo na liście wszystkich autorów piszących w serwisie.
Obiekty składowe są szczególnym przypadkiem złożonych atrybutów.
Przykład
<div class="Article"> <div class="Source">...</div> ... <div class="Author">...</div> </div>
Rozszerzanie
W programowaniu zorientowanych obiektowo termin ten tłumaczy się częściej jako "dziedziczenie", jednak celowo używam tutaj innego pojęcia (ang. extends), aby nie myliło się z dziedziczeniem CSS, związanym z kaskadowością stylów.
Człowiek od zawsze miał potrzebę kategoryzowania otaczającej go rzeczywistości. Dzięki temu możemy łatwiej i precyzyjniej komunikować się ze sobą. Kategorie znaczeniowe, w jakie grupujemy otaczające nas obiekty, zwykle odzwierciedlają pewną hierarchię, opisującą różne poziomy ogólności poszczególnych pojęć. Na przykład laptop jest rodzajem komputera, a komputer jest rodzajem urządzenia elektronicznego.
Z punktu widzenia rozszerzania obiektów w CSS chodzi o to, aby stylizować elementy na różnym poziomie ogólności w zależności od ich przeznaczenia w serwisie. Przykładowo jeśli mamy już gotowy ogólny artykuł:
<div class="Article">...</div>
ale potrzebujemy jeszcze prawie identycznie wyglądającego artykułu recenzji (zawierającego dodatkowo np. ocenę redakcyjną), nie ma sensu tworzyć go od zera. Znacznie lepiej jest wykorzystać całą stylizację zapisaną w ogólnym artykule (Article), a osobno dodać tylko nowe rzeczy (Review):
<div class="Article Review">...</div>
Mówimy, że Article jest klasą bazową, którą rozszerza klasa pochodna Review.
Dzięki tej technice, unikając powtórzeń deklaracji, zmniejszysz rozmiary arkusza CSS (szybsze ładowanie, mniejszy transfer), a przede wszystkim przyspieszysz sposób zarządzania zmianami, ponieważ modyfikacja definicji klasy Article od razu zmieni elementy class="Article" oraz class="Article Review", tak aby pasowały do nowego szablonu witryny, a jednocześnie modyfikując klasę Review nie musisz się obawiać, że zepsujesz coś w elementach oznaczonych jako class="Article". Oczywiście możliwe jest rozszerzanie na większej liczbie poziomów zagnieżdżenia.
Wielokrotne rozszerzanie
Teoretycznie klasa pochodna może rozszerzać więcej niż jedną klasę bazową. Może to jednak wywołać trudne do przewidzenia konsekwencje przy zmianach deklaracji w klasach bazowych. Jeśli koniecznie chcesz skorzystać z tej techniki, klasy bazowe powinny być raczej maksymalnie uproszczone.
<div class="Information Important Warning">...</div>
W powyższym przykładzie klasa pochodna Warning (ostrzeżenie) ma dwie klasy bazowe: Information (informacja) oraz Important (ważne). Jednocześnie Important nie jest pochodną Information ani na odwrót - są to zupełnie niezależne klasy. Klasa pochodna (Warning) przejmuje stylizację zarówno z klasy Information (np. tekst ujęty w ramkę) jak i Important (np. czerwony tekst albo wytłuszczenie).
Przesłanianie
Co zrobić, jeśli recenzja - szczególny przypadek ogólnego artykułu - ma wyglądać jednak nieznacznie inaczej od swojego pierwowzoru (klasy bazowej)? Wybrane deklaracje z klasy bazowej można przesłonić (ang. override), przypisując im zmienione wartości w klasie pochodnej. Na przykład:
.Article { color: black; background-color: white; /* ... */ } .Review { color: green; font-size: 12px; /* ... */ }
Pamiętaj jednak, że kolejność wymieniania nazw klas w atrybucie class="..."
z punktu widzenia kaskadowości stylów nie ma znaczenia, dlatego należy zadbać, aby w arkuszu stylów definicja klasy Review znajdowała się później niż Article.
Ten prosty zabieg pozwoli nam uzyskać recenzję z zielonym tekstem, przy zachowaniu białego tła oraz ewentualnych wielu innych deklaracji, zdefiniowanych w klasie bazowej. W ten sposób, nawet przy istnieniu drobnych różnic wyglądu, nadal możemy korzystać z techniki rozszerzania cech obiektów, oszczędzając sobie sporo niepotrzebnego pisania. Gdyby jednak oba rodzaje artykułu wyglądały zupełnie inaczej, nie postępuj w ten sposób, gdyż tylko utrudni Ci to pracę.
Klasa abstrakcyjna
Klasa abstrakcyjna (ang. abstract class) jest przeznaczona wyłącznie do rozszerzania przez klasy pochodne, a nie do samodzielnego użycia w dokumencie. Jeśli w serwisie mamy więcej rodzajów publikacji, zapewne chcielibyśmy, aby każda z nich wyglądała podobnie. Takie wspólne deklaracje stylów możemy zdefiniować w klasie abstrakcyjnej Publication, a potem używać następująco:
<div class="Publication Article">...</div> <div class="Publication Article Review">...</div>
Rożnica pomiędzy zwykłą klasą bazową (np. Article) a klasą abstrakcyjną jest taka, że nigdy w dokumencie nie pojawi się samodzielnie w postaci class="Publication". Mimo tego jest niezwykle przydatna, gdyż dzięki niej w jednym miejscu mamy zebrane deklaracje ogólnie stylizujące wszystkie publikacje w serwisie.
Singleton
Singleton to jeden ze znanych wzorców projektowych w programowaniu zorientowanym obiektowo. Jego celem jest zapewnienie, że w danym programie nigdy nie powstanie więcej niż jedna instancja określonej klasy. Na gruncie CSS zorientowanego obiektowo można to przyrównać do selektora identyfikatora, ponieważ w pojedynczym dokumencie HTML nie mogą istnieć dwa elementy o takim samym identyfikatorze (z identycznym atrybutem id="..."
).
Polimorfizm
Na koniec jeszcze jedna analogia do terminologii programowania zorientowanego obiektowo, znanego z niemal każdego nowoczesnego języka programowania. Stosowanie kontekstu selektorów można porównać do polimorfizmu, tzn. efekt działania tej samej deklaracji (klasy) zależy od typu elementu, do którego się odwołujemy. Na przykład:
<div class="Information Warning">...</div>
może działać inaczej niż
<span class="Information Warning">...</span>
chociaż nazwa przypisanej klasy CSS jest w obu przypadkach identyczna:
.Information { background-color: white; } div.Warning { color: red; } span.Warning { font-weight: bold; }