XHTML i CSS

Tomasz Staniak


Żeby zrozumieć jak działa dziedziczenie, musimy spojrzeć na dokument, jak na drzewo genealogiczne elementów. Mamy rodziców, dzieci i potomków. Elementy mogą być sąsiadami bądź rodzeństwem.

Numer 15 (1/2006) • 25 stycznia 2006


Jinyoung Shin : Minigaleria

Tomasz Staniak. Wprowadzenie w świat kaskadowych arkuszy stylów.

Ilustracja: Jinyoung Shin


Wprowadzenie w świat kaskadowych arkuszy stylów


Z poprzedniego numeru wiemy już, że powinniśmy zadbać o oddzielenie struktury dokumentu od jej treści i prezentacji. Wiemy również, że w nowoczesnych stronach WWW za warstwę prezentacyjną odpowiada CSS.

Choć CSS nie jest skomplikowanym językiem - na początku możemy mieć z nim wiele kłopotów. Przede wszystkim, CSS nie jest to tylko zbiór kilkudziesięciu przypadkowych znaczników, klas i atrybutów.

Na zrozumienie sposobu działania, składa się, przede wszystkim, zrozumienie pojęć takich jak box-model, kaskada, specyficzność, dziedziczenie czy selektory.

Kaskada

W większości systemów operacyjnych, czy też w niektórych programach, występuje opcja kaskadowego rozmieszczenia okien - okna nachodzą wtedy na siebie.

Podobnie jak okna, tak i arkusze stylów i reguły w nich zawarte, układają się w podobną kaskadę, przenikają się, uzupełniają, tworząc jeden "wirtualny", "globalny" styl.

Jak podaje Wikipedia:

Nazwa "kaskadowy" wynika z faktu, że istnieją ściśle określone zasady wyboru sposobu wyświetlania danego elementu strony jeśli jest on zdefiniowany inaczej przez zewnętrzny arkusz, następnie wewnętrzny i wreszcie lokalnie. Przyjęto, że oddziaływanie stylów z arkuszy zewnętrznych może być modyfikowane przez style zdefiniowane w nagłówku dokumentu, to zaś może być modyfikowane przez style zdefiniowane bezpośrednio w ciele dokumentu. Pierwszeństwo mają zatem style zdefiniowane "bliżej" konkretnego elementu. Przeglądarka sprawdza więc najpierw, czy istnieją jakieś arkusze zewnętrzne i stosownie do ich definicji formatuje stronę. Następnie sprawdza, jakie są definicje stylów w nagłówku strony i modyfikuje wygląd zgodnie z ich ustaleniami. Następnie sprawdza style w samym dokumencie i ponownie modyfikuje fizyczną postać strony. To oczywiście model działania, ale pokazuje on, jak działa taka kaskada stylów. Między stylami z różnych źródeł nie muszą zresztą wcale występować żadne konflikty - wszystkie style uzupełnią się, tworząc jeden wielki, "wirtualny" styl.

Zatem wymieniając kolejność parsowania i aplikowana stylów do elementów strony:

  1. <link rel="stylesheet" type="text/css" href="main.css" media="all" />
  2. <style type="text/css"> body {font-size: 12px;} </style>
  3. <div style="font-size: 10px;">treść</div>

Ale “siła" definicji jest odwrotna:

  1. atrybut !important
  2. <div style="font-size: 10px;">treść</div>
  3. <style type="text/css"> body {font-size: 12px;} </style>
  4. <link rel="stylesheet" type="text/css" href="main.css" media="all" />

Atrybut !important wskazuje elementy, które są najważniejsze, niezależnie od ich pozycji w kaskadzie.

Arkusze stylów importowane przez @import posiadają taką samą pozycję w kaskadzie jak arkusz, z którego zostały wywołane.

Warto wspomnieć również o tym, że każda przeglądarka ma swoje style dla różnych elementów, użytkownik odwiedzający naszą stronę również może mieć zdefiniowany własny styl (user stylesheets).

W takim przypadku, najważniejszy jest arkusz stylów zdefiniowanych przez autora strony. Mniej ważny jest arkusz stylów zdefiniowanych przez użytkownika, a najmniej ważny jest domyślny styl przeglądarki.

Specyficzność

O specyficzności pisało już wielu autorów, wciąż jednak bardzo często można spotkać się z brakiem zrozumienia jak działa specyficzność elementów CSS.

W zasadzie, specyficzność nie jest niezbędna do stosowania CSS - wielu autorów z powodzeniem tworzy strony z wykorzystaniem CSS, nie mając nawet pojęcia o tym, że coś takiego istnieje.

Jak to działa?

Specyficzność to całkiem prosty algorytm, umożliwiający przeglądarce prawidłowe zinterpretowanie kodu CSS - określenie, która reguła jest ważniejsza oraz co i jak powinno zostać sformatowane.

Do obliczenia specyficzności danej regułki, musimy rozbić ją na trzy podstawowe elementy:

Powstaje nam zatem prosta tabelka:

Specyficzność: podstawowe elementy
Regułka Liczba identyfikatorów Liczba klas Liczba tagów HTML

Wyobraźmy sobie zatem, że mamy na naszej stronie, w kodzie, prosty układ:

  1. <div id="container">
  2. <p class="text">Lorem ipsum</p>
  3. </div>

i trzy regułki stylujące:

  1. p {color: red;}
  2. p.text {color: blue;}
  3. #container p.text {color: green;}

Wypełniając naszą tabelę, otrzymujemy:

Specyficzność: reguły stylujące
Regułka CSS Liczba identyfikatorów Liczba klas Liczba tagów
p 0 0 1
p.text 0 1 1
#container p.text 1 1 1

Jak to rozumieć?

W powyższym stylowaniu, domyślnie, tekst we wszystkich akapitach na stronie, będzie koloru czerwonego. Te, które posiadają klasę "text", będą koloru niebieskiego a akapit o klasie "text" znajdujący się w sekcji o identyfikatorze "container" będzie koloru zielonego.

A co ze stylami inline?

W przypadku stylów inline, czyli zawartych między znacznikami <style type="text/css"> a </style> w kodzie strony, do naszej tabelki dochodzi jeszcze jedna kolumna:

Specyficzność: style inline
Regułka CSS Styl inline Liczba identyfikatorów Liczba klas Liczba tagów
p 0 0 0 1
p.text 0 0 1 1
#container p.text 0 1 1 1

Jeśli napisalibyśmy w kodzie strony:

  1. <style type="text/css">
  2. p {color: black;}
  3. </style>

to ta definicja miałaby wartość 1,0,0,1 - skutecznie zmieniając kolor tekstu we wszystkich akapitach na czarny.

Selektory i pseudo-klasy

Selektor uniwersalny (* - gwiazdka) ma specyficzność równą 0.

Zgodnie ze specyfikacją CSS2.1 (www.w3.org/TR/CSS21/cascade.html#specificity) pseudo element posiada specyficzność taką samą jak element (0,0,0,1), pseudo-klasa taką samą jak klasa (0,0,1,0) a selektor atrybutów również taką samą jak klasa.

W sposób bardzo wesoły i opisowy, specyficzność przedstawił Malarkey (www.stuffandnonsense.co.uk/archives/css_specificity_wars.html)

Dziedziczenie

Definiowanie atrybutów dla każdego z elementów występujących na stronie byłoby czasochłonne i nieefektywne, dlatego właśnie, w CSS występuje mechanizm zwany dziedziczeniem.

Żeby zrozumieć jak działa dziedziczenie, musimy spojrzeć na dokument, jak na drzewo genealogiczne elementów. Mamy rodziców, dzieci i potomków. Elementy mogą być sąsiadami bądź rodzeństwem.

Dzięki temu, że, tak jak w życiu, potomkowie dziedziczą cechy po rodzicach, możemy ograniczyć ilość stosowanych klas, identyfikatorów i atrybutów.

Skoro wiemy już, że wartości są dziedziczone, możemy przestać definiować rozmiar i rodzinę fontu, kolor czy rodzaj obramowania w każdym potomnym elemencie.

Wystarczy, że zadeklarujemy, np.: styl fontu raz:

  1. body
  2. {
  3. font-size: 12px;
  4. font-family: sans-serif;
  5. }

A w pozostałych elementach będziemy stosować tylko odniesienia relatywne, np.:

  1. h2
  2. {
  3. font-size: 160%;
  4. }

Bądź wprowadzimy odpowiednią informację, tylko w sytuacji, gdy dane formatowanie, w elemencie potomnym, się zmienia:

  1. #elementNadrzędny
  2. {
  3. padding: 16px 0;
  4. }
  5. #któryśElementPodrzędny
  6. {
  7. padding: 0 4px;
  8. }

Nasz arkusz stylów, zyska w ten sposób odpowiednią elastyczność, która pozwoli nam na bezstresowe wprowadzanie kosmetycznych zmian, gdy zajdzie taka konieczność.

Box-model

Większość ludzi, przyzwyczajona do tego, jak działa IE na stronach bez doctype, pierwsze włosy i nerwy traci już na box-modelu.

W CSS elementy dzielimy na liniowe (inline - pogrubienia, linki itp) i blokowe (block - nagłówki, akapity). Kluczem do zrozumienia box-modelu jest fakt, że mamy cztery czynniki wpływające na wymiary bloku.

Szerokość, wysokość i ramkę możemy zobaczyć - nadając im wymiary i kolor. Margines jest niewidzialny. Wszystkie te wymiary się sumują. Czyli, jeśli chcemy zrobić na stronie box, o wymiarach 320×240, z dwupikselowym marginesem, paddingiem 10px i jednopikselową ramką, musimy dodać te wszystkie wartości do siebie, pamiętając, że definiując, np.: padding: 10px; tak naprawdę “dochodzi" nam 20 pikseli w poziomie i 20 pikseli w pionie.

W takim przypadku, większość początkujących napisze:

  1. #box
  2. {
  3. border: 1px solid #000;
  4. margin: 2px;
  5. padding: 10px;
  6. width: 320px;
  7. height: 240px;
  8. }

Jest to oczywiście źle. Po prostej kalkulacji otrzymujemy, bowiem, że ramka zabiera nam 2 piksele, margines to 4 piksele a padding to 20 pikseli. Dochodzi nam, zatem 26 pikseli w pionie i poziomie, które, jeśli chcemy, by nasz element miał wcześniej podane wymiary, musimy odjąć od zdefiniowanych wartości.

Nasz poprawny kod będzie wyglądał tak:

  1. #box
  2. {
  3. border: 1px solid #000;
  4. margin: 2px;
  5. padding: 10px;
  6. width: 294px;
  7. height: 214px;
  8. }

Dopóki nie nauczysz się tworzyć z pomocą krótkich atrybutów, stosuj długie (np.: padding-left, padding-right, padding-top, padding-bottom) - łatwiej będzie Ci zrozumieć, o co chodzi.

Nim zaczniesz narzekać na różnice w wyglądzie stron między IE a innymi przeglądarkami, sprawdź, czy w kodzie strony znajduje się deklaracja doctype i czy nie poprzedza jej prolog XML.

Hacki

Prawdopodobnie na zawsze pozostanie tajemnicą, dlaczego Microsoft nie zdecydował się na szersze zastosowanie, i dalszy rozwój, całkiem udanego, silnika napędzającego IE - Tasman (IE5/Mac).

Dominacja Internet Explorera a zarazem - wynikające z tego stanu komplikacje (błędny box-model, brak obsługi wielu atrybutów, różnice w interpretacji kodu, kaskady, specyficzności) sprawiły, że powstała cała masa sztuczek, umożliwiających ominięcie problemów z Internet Explorerem.

Przez wiele lat, ratowały one skórę deweloperom i minimalizowały stres związany z tworzeniem stron WWW.

Ostatnie zapowiedzi ze strony dev-teamu odpowiedzialnego za IE7 wskazują, że w najnowszym wydaniu Internet Explorera nie będziemy już mieli takiego pola do popisu w oszukiwaniu przeglądarki. Co bardziej skrajni developerzy zapowiadają apokalipsę i już zaczynają zliczać ile stron będzie wymagało przebudowy.

Czy tak się stanie w rzeczywistości - wypada nam poczekać i się przekonać. Jedno jest pewne - jeśli właśnie składasz stronę, zrezygnuj z większości, starych, hacków na Internet Explorera (np.: "* html" w IE7 poprawiono drzewo DOM).

Na obecną chwilę, godnym zaufania hackiem, wydaje się być "underscore hack", polegający, po prostu, na dodaniu znaku podkreślenia, przed atrybutem serwowanym dla IE (Windows).

Na przykład tak:

  1. p
  2. {
  3. margin: 6px 0;
  4. _margin: 8px 0; /* tę wartość przyjmie akapit w IE/Windows */
  5. }

Choć walidatory CSS mogą poinformować, że "underscore hack" psuje walidację, jest to nieprawda - znak podkreślenia jest zawarty w specyfikacji CSS i przewidziany jako element poprzedzający rozszerzenia autorów przeglądarek. Zatem nie jest to błąd, co najwyżej - delikatne nadużycie.

Najlepiej jednak, przenieść reguły specjalne dla IE, do osobnego arkusza stylów i skorzystanie z jednego z najlepszych rozwiązań, jakie zaoferował Microsoft w swoim produkcie - komentarzy warunkowych.

Pojawia się oczywiste pytanie: czy stosować hacki?

Jak słusznie zauważył Dan Cederholm - hack nie jest czymś złym, tak naprawdę. Gdybyśmy żyli w idealnym świecie, w którym wszystkie przeglądarki byłyby zgodne w 100% ze standardami i między sobą - niewykluczone, że nie musielibyśmy w ogóle myśleć o próbach obejścia różnic w interpretacji kodu.

W idealnym świecie nie byłoby również głodu, wojen i niesprawiedliwości, więc pora zejść nam na ziemię i przyznać, że w rzeczywistości, hacki są czymś całkowicie naturalnym.

Problem sprawia sam termin - hack, ludziom kojarzy się negatywnie. Jeśli powiemy klientowi, że by zrealizować jego wizję, musimy zastosować kilka hacków, prawie na pewno skojarzy mu się to z czymś złym, czasochłonnym i przede wszystkim - drogim.

Lepszym słowem byłby z pewnością "workaround" (obejście), bądź "realign" lecz to nie ja wymyślam terminologię.

Nieoficjalne rozszerzenia

O rozszerzeniach specyfikacji w Internet Explorerze wie niemal każdy (niesławne filter: chociażby)

Mozilla i Opera również wprowadziły swoje rozszerzenia w swoich przeglądarkach. Przoduje w tym Mozilla, której listę rozszerzeń możemy znaleźć tutaj: developer.mozilla.org/en/docs/Mozilla_CSS_Extensions

Opera ogranicza się do 3 elementów, przydatnych głównie w przypadku pracy na plikach XML.

Tomasz Staniak


O autorze

Tomasz Staniak

Tomasz Staniak

Zapytany czym się dotychczas zajmował, nie potrafi udzielić jednoznacznej odpowiedzi. Był już zwykłym pracownikiem fizycznym, audytorem w dużej sieci hipermarketów, menadżerem w branży gier komputerowych i próbował prowadzić własną grupę. Obecnie pracuje jako webdeveloper w easy.web oraz prowadzi bloga, w znacznej mierze poświęconego standardom sieciowym.