Źródła stylów#
Styl wewnętrzny#
<head> ... <style type="text/css"> ... </style> ... </head>
Reguły CSS można umieszczać bezpośrednio w plikach HTML czy XML. Ten rodzaj arkuszy stylów nazywa się też dokumentowym arkuszem stylów (document style sheet) lub osadzonym arkuszem stylów (embedded style sheet).
Spoiwem łączącym dokument z wewnętrznym arkuszem stylów jest znacznik <style>
, którego działanie omówiłem w kursie HTML 4.01 (dział "Spis poleceń HTML - STYLE"). Znacznik ten umieszczamy wewnątrz nagłówka dokumentu (<head>...<head>
), w dowolnej ilości, przykładowo:
<head>
<style type="text/css">
body {background: gray;}
p {color: green;}
</style>
<style type="text/css">
h1 {color: red;}
h2 {color: green; background: white;}
h3 {font: italic 2em Times, serif;}
</style>
<head>
W HTML5 podawanie atrybutu type="text/css"
wewnątrz znacznika <style>
nie jest już konieczne.
Zawężony styl wewnętrzny#
<element> ... <style type="text/css" scoped> ... </style> ... </element>
Selektory muszą rozpoczynać poszukiwanie od konkretnego elementu, wartości stylów mogą być dziedziczone, dlatego też istotnym jest, od którego strukturalnego elementu wszystko będzie się odbywało. Całość najłatwiej wyjaśnić na przykładzie elementu style
, który w HTML5 został nieco zmodyfikowany.
W przypadku HTML 4.01 i początkowych wersjach CSS element style
dopuszczalny jest jedynie wewnątrz elementu head
. W takim wypadku reguły maja zastosowanie począwszy od elementu korzenia, co w przypadku (X)HTML oznacza element html
. Dzięki selektorom możemy stopniowo zawężać wybór elementów do konkretnych przypadków.
HTML5 wprowadza w elemencie style
dodatkowy logiczny atrybut scoped
, który decyduje o tzw. zawężeniu # (scoped) reguł do innego elementu. Brak atrybutu w elemencie style
jest równoważny z poprzednim rozwiązaniem, czyli style zawężone zostają do elementu html
. Obecność atrybutu zezwala na umieszczenie elementu sytle
w innym elemencie, i w konsekwencji zawęża reguły jedynie do poddrzewa zakorzenionego w elemencie, który jest rodzicem elementu style
.
Jeśli atrybut scoped
jest obecny i element style
posiada rodzica w postaci innego elementu, to element style
musi poprzedzać wszelką przepływającą zawartość w swoim elemencie rodzica, za wyjątkiem odstępów między elementami i innych elementów style
, oraz model zawartości elementu rodzica nie może zawierać transparentnych komponentów.
Konsekwencją powyższego jest to, że zawężone elementy style
nie mogą być dziećmi chociażby w elementach <a>
lub <ins>
, nawet jeśli te używane są jako kontenery przepływającej zawartości.
Zamiast skomplikowanych definicji najlepiej przeanalizować poniższy przykład (działa tylko w przeglądarce Firefox):
<!DOCTYPE html>
<html>
<head>
<style>
li {color: blue;}
:scope {
border: 5px solid blue;
}
</style>
</head>
<body>
<ul>
<style scoped>
li {color: red;}
:scope {
border: 5px solid red;
}
</style>
<li>Jawnie zawężone style (kolor czerwony)</li>
<li>Jawnie zawężone style (kolor czerwony)</li>
<li>Jawnie zawężone style (kolor czerwony)</li>
</ul>
<ul>
<li>Domyślnie zawężone style (kolor niebieski)</li>
<li>Domyślnie zawężone style (kolor niebieski)</li>
<li>Domyślnie zawężone style (kolor niebieski)</li>
</ul>
</body>
</html>
Przykład można opisać następująco:
- Pierwszy element
style
(wewnątrzhead
) nie posiada atrybutuscoped
, to też domyślnie zawężony zostaje do elementuhtml
. Selektorli
wybiera wszystkie elementy list, począwszy odhtml
, po czym nadaje im niebieski kolor tekstu. - Drugi element
style
(wewnątrz pierwszej listyul
) posiada atrybutuscoped
, to też zawężony zostaje do tej listy. Selektorli
wybiera wszystkie elementy list, począwszy od zawężającej pierwszej listy, po czym nadaje im czerwony kolor tekstu. Reguła z drugiego elementustyle
nadpisze tę z pierwszego, a to ze względu na drugi krok w kaskadzie. - Za pomocą pseudoklasy
:scope
można w prosty sposób oznaczyć zawężający element. Pierwszy zawężający element otrzymał obramowanie niebieskie, kiedy drugiemu przypisałem obramowanie czerwone.
Zawężające elementy mogą być pozagnieżdzane, co w efekcie tworzy między nimi relację przodek/potomek. Oczywiście może dochodzić do konfliktów między identycznymi regułami pochodzącymi z różnych elementów style
. Prawidłowa obsługa takiej sytuacji znajduje się w opisie drugiego kroku kaskady. Na chwilę obecną jedynie Firefox obsługuje zawężenia CSS, i wydaje się, że wszystko przebiega zgodnie z wytycznymi (z uwzględnieniem adnotacji !important
):
<!DOCTYPE html>
<html>
<head>
<style>
* {color: gray;}
</style>
</head>
<body>
<div>
<style type="text/css" scoped>
div {color: red;}
</style>
Pierwszy kontener DIV (kolor czerwony).
<div>
<style type="text/css" scoped>
div {color: blue;}
</style>
Drugi kontener DIV (kolor niebieski).
<div>
<style type="text/css" scoped>
div {color: green;}
</style>
Trzeci kontener DIV (kolor zielony).
</div>
</div>
</div>
<p>Akapit poza kontenerami (kolor szary).</p>
</body>
</html>
Zachęcam do ręcznego dopisywania adnotacji !important
, zaczynając od najbardziej zagnieżdżonego elementu style
, czyli modyfikując w nim regułę do postaci div {color: green !important;}
, uruchomienia kodu, a następnie do stopniowego przechodzenia wyżej, aż otrzymamy regułę div {color: gray !important;}
. W ostateczności powinniśmy otrzymać szary kolor we wszystkich elementach, co ewidentnie podkreśla fakt, że w przypadku zawężonych stylów i adnotacji !important
zewnętrzne reguły wygrywają.
Na podstawie przykładu można zauważyć, że element style
bez atrybutu scoped
również powoduje zawężenie, z tym że jest ono domyślne (do elementu html
). Można zatem powiedzieć, że wszystkie elementy zawężające powyższego przykładu spełniają relację przodkek/potomek, dlatego też dopisanie !important
w pierwszym elemencie style
nadpisuje wszystkie pozostałe (nawet te z !important
).
Oczywiście można utworzyć dwa zawężenia, które nie spełniają między sobą relacji przodek/potomek, ale z zachowaniem tej relacji w przypadku elementu style
umieszczonego w elemencie head
. W poniższym przykładzie najbardziej znaczącą regułę od razu oznaczyłem ważnością:
<!DOCTYPE html>
<html>
<head>
<style>
* {color: gray !important;}
</style>
</head>
<body>
<div>
<style type="text/css" scoped>
div {color: red;}
</style>
Pierwszy kontener DIV (utracony kolor czerwony).
</div>
<div>
<style type="text/css" scoped>
div {color: blue;}
</style>
Drugi kontener DIV (utracony kolor niebieski).
</div>
<p>Akapit poza kontenerami (kolor szary).</p>
</body>
</html>
Tym razem wystarczy usunąć polecenie !important
w pierwszym elemencie style
, a pozostałe zawężające elementy (niezależne od siebie) odzyskają swój kolor.
Reguła @global#
Wewnątrz zawężonych zasobów CSS autorzy mogą korzystać z reguły @global
, która stanowi wyspecjalizowaną odmianę reguły małpy.
Poniższa produkcja została dodana do gramatyki:
global : GLOBAL_SYM S* ruleset ;
Następujące reguły zostały dodane do tokenizera Flex:
B b|\\0{0,4}(42|62)(\r\n|[ \t\r\n\f])? @{G}{L}{O}{B}{A}{L} {return GLOBAL_SYM;}
Selektory proste w zestawach reguł umieszczonych wewnątrz zawężonych zasobów CSS, które poprzedzone zostaną regułą @global
, muszą być przetwarzane w taki sam sposób, jak normalne zestawy reguł w niezawężonych zasobach CSS.
Selektory proste wewnątrz zawężonych zasobów CSS, które nie zostały poprzedzone regułą @global
, muszą odpowiadać jedynie elementowi rodzica style
(jeśli istnieje), oraz wszystkim potomkom tego elementu.
Upraszczając sprawę, polecenie @global
pozwala definiować reguły wewnątrz zawężonych elementów style
, które będą aplikowane poza zawężeniem:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<style type="text/css" scoped>
div {color: red;}
@global * {color: blue}
</style>
Pierwszy kontener DIV (kolor czerwony).
</div>
<p>Akapit poza kontenerami (kolor niebieski).</p>
</body>
</html>
Na chwilę obecną żadna aktualna przeglądarka nie obsługuje dyrektywy @global
, dlatego dokładniejsze testy wykonane zostaną w późniejszym czasie.
Praktyczne przypadki użycia#
Zawężanie reguł CSS do konkretnych elementów nie powstało z byle powodów. To może być szczególnie użyteczne w przypadku wymieszania treści o różnym pochodzeniu: jeśli chcemy, jako autorzy strony internetowej, dołączyć do niej zawartość pochodzącą od osób trzecich, włączając tym samym wszystkie jej style, to zawsze ryzykujemy tym, że obce reguły mogą gryźć się z naszymi poleceniami, i w najgorszym wypadków nadpisywać style niepowiązane z treścią zewnętrzną. Zaletą zawężenia jest możliwość łączenia zawartości z innych stron, takich jak facebook, twitter, ebay, itd., bezpośrednio w pojedynczej stronie, bez konieczności izolowania ich za pomocą iframe
lub edytowania - na bieżąco - zewnętrznej zawartości.
W przypadku systemów CSM, które pozwalają na wysyłanie znaczników bezpośrednio osadzanych w finalnej stronie przeznaczonej do wyświetlenia, zawężenie jest wspaniałą cechą pozwalającą upewnić się, że każdy taki fragment zostanie odizolowany od pozostałych części strony. Może być to również użyteczne dla systemów opartych na wiki.
Kiedy chcemy zaprezentować jakieś ciekawe demo na już gotowej stronie (np. blogu), to łatwo jest ograniczyć style tylko do zawartości dema. Dzięki temu demo może stosować bogatą stylizację, bez obawy, że będzie ona miały wpływ na pozostałe części strony.
Kolejną sprawą jest po prostu hermetyzacja (encapsulation): dla przykładu, jeśli strona posiada menu boczne, to warto przy użyciu zawężenia umieścić reguły CSS, które będą specyficzne tylko dla tego menu. Reguły te nie będą miały żadnego wpływu na renderowanie pozostałych części strony, przez co całe menu zostanie odseparowane od głównej treści.
Być może jednym z najbardziej interesujących przypadków jest użycie zawężenia dla komponentów webowych. Komponenty webowe będą doskonałym sposobem do budowania takich rozwiązań jak suwaki, menu, kalendarz z możliwością wyboru daty, zakładki, i wiele innych. Dzięki zawężeniu projektant może zbudować widżet i opakować go razem z własnymi stylami, w formie samodzielnej jednostki, którą inni mogą pobrać i wykorzystać przy tworzeniu bogatej aplikacji internetowej. Do tej pory nie istniał żaden sensowny/wygodny sposób ograniczający style w przypadku komponentów webowych, no chyba że zaczęliśmy uciekać się do złych praktyk, takich jak stylizacja lokalna.
Obsługa starszych przeglądarek#
Przeglądarki nieobsługujące języka CSS prawdopodobnie zignorują elementy <style>
. Jednak deklaracje w nich zawarte mogą nie zostać pominięte, ponieważ dla przeglądarki wyglądają one jak zwykły tekst. Sprawi to, że deklaracje stylów zostaną wyświetlone na początku strony. Oczywiście przeglądarka powinna zignorować taki tekst, ponieważ nie jest on częścią ciała dokumentu (znacznik <body>
), ale tak się zazwyczaj nie dzieje.
W celu poradzenia sobie z tym problemem zalecało się umieszczenie wszystkich reguł CSS wewnątrz komentarza HTML. Taki zabieg powinien zmusić starsze przeglądarki do całkowitego zignorowania nie tylko elementów <style>
, ale i deklaracji stylów, ponieważ zawartość komentarzy HTML nie zostanie wyświetlona. Jednocześnie przeglądarki obsługujące CSS nadal będą w stanie poprawnie odczytywać arkusze stylów. Szczegóły tego mechanizmu dokładnie omówiłem w kursie HTML 4.01 (dział "Formatowanie treści - Komentarze").
Prosty przykład (zgodny z XML-em):
<head>
<style type="text/css">
/* <![CDATA[ */
<!--
body {background-color: white;}
a:link {color: navy;}
a:visited {color: green;}
a:active {color: red;}
-->
/* ]]> */
</style>
</head>
Na dzień dzisiejszy odchodzi się od tej praktyki, mało która przeglądarka nie wspiera języka CSS.
Podsumowanie#
Style osadzone są mniej elastyczne od stylów zewnętrznych. Najczęściej stosowane są w przypadku stron, którym należy nadać unikatowe formatowanie - inne niż to zawarte w ogólnym zewnętrznym arkuszu CSS.
W dodatku trzeba sobie zdawać sprawę z tego, że reguły CSS umieszczane bezpośrednio w kodzie strony automatycznie zwiększaj rozmiar pobieranych plików. W przypadku dynamicznych rozwiązań (wszelkiej maści systemów CMS) pliki HTML najczęściej nie są cache'owane, dlatego duża liczba osadzonych reguł CSS zwiększa ilość ściganych danych z każdym żądaniem takich dokumentów. Problem można zniwelować poprzez włączenie kompresji po stronie serwera, lub ewentualnie importując style do wewnętrznego arkusza, ale wiąże się to z dodatkowym żądaniem HTTP, negatywnie wpływającym na czas ładowania strony.