3.2. Specyficzność selektorów: Obliczanie i rozwiązywanie konfliktów
Wprowadzenie: Kto ma ostatnie słowo?
W poprzedniej lekcji dowiedzieliśmy się, że kaskada CSS bierze pod uwagę źródło stylów, ważność (!important
) i kolejność reguł. Jednak gdy wiele reguł z tego samego źródła i o tej samej ważności dotyczy tej samej właściwości elementu, kluczową rolę w rozstrzyganiu konfliktu odgrywa **specyficzność selektora**.
Specyficzność to "waga" lub "moc" danego selektora CSS. Im bardziej specyficzny (precyzyjny) jest selektor, tym większy ma priorytet w kaskadzie. Przeglądarka oblicza specyficzność dla każdego selektora pasującego do elementu i stosuje styl z reguły o najwyższej specyficzności.
Obliczanie specyficzności: Notacja (A, B, C, D)
Specyficzność jest zazwyczaj reprezentowana jako czteroczęściowa wartość (A, B, C, D), gdzie każda część liczy wystąpienia określonego typu selektora. Porównanie odbywa się od lewej do prawej (A jest najważniejsze).
- A (Style inline): Liczba stylów inline zastosowanych do elementu. Styl inline (
style="..."
) ma najwyższą specyficzność (1, 0, 0, 0). - B (Selektory ID): Liczba selektorów ID (np.
#mojeID
) w całym selektorze. - C (Selektory klas, atrybutów i pseudoklas): Liczba selektorów klas (np.
.mojaKlasa
), selektorów atrybutów (np.[type="text"]
) i pseudoklas (np.:hover
,:nth-child()
) w całym selektorze. - D (Selektory typu i pseudoelementy): Liczba selektorów typu (np.
div
,p
) i pseudoelementów (np.::before
,::first-line
) w całym selektorze.
Ważne uwagi:
- Selektor uniwersalny (
*
) i kombinatory (>
,+
,~
, spacja) **nie wpływają** na specyficzność. - Pseudoklasa
:not()
sama w sobie nie liczy się do specyficzności, ale liczą się selektory **wewnątrz** niej. - Deklaracje z
!important
**ignorują** specyficzność (ale podlegają własnym regułom priorytetu źródła, jak omówiono w poprzedniej lekcji).
Przykłady obliczania specyficzności:
Selektor | A (Inline) | B (ID) | C (Klasy, Attr, Pseudo-klasy) | D (Typy, Pseudo-elementy) | Specyficzność (A,B,C,D) |
---|---|---|---|---|---|
style="..." |
1 | 0 | 0 | 0 | (1,0,0,0) |
#nav |
0 | 1 | 0 | 0 | (0,1,0,0) |
#nav ul li a.active |
0 | 1 | 1 | 3 | (0,1,1,3) |
div.box p |
0 | 0 | 1 | 2 | (0,0,1,2) |
a:hover |
0 | 0 | 1 | 1 | (0,0,1,1) |
input[type="text"] |
0 | 0 | 1 | 1 | (0,0,1,1) |
.menu li |
0 | 0 | 1 | 1 | (0,0,1,1) |
p |
0 | 0 | 0 | 1 | (0,0,0,1) |
* |
0 | 0 | 0 | 0 | (0,0,0,0) |
Rozwiązywanie konfliktów
Gdy przeglądarka porównuje specyficzność dwóch reguł dotyczących tej samej właściwości:
- Porównuje wartość A. Jeśli jedna reguła ma A=1, a druga A=0, wygrywa ta pierwsza (styl inline).
- Jeśli wartości A są równe (zazwyczaj obie 0), porównuje wartość B. Reguła z większą liczbą selektorów ID wygrywa.
- Jeśli wartości B są równe, porównuje wartość C. Reguła z większą liczbą klas, atrybutów i pseudoklas wygrywa.
- Jeśli wartości C są równe, porównuje wartość D. Reguła z większą liczbą typów i pseudoelementów wygrywa.
- Jeśli wartości A, B, C i D są **identyczne**, to ostatecznie decyduje **kolejność w kodzie CSS** – wygrywa reguła zdefiniowana później.
Przykład konfliktu:
<button class="przycisk anuluj" id="przycisk-reset">Resetuj</button>
/* Reguła 1 */
.przycisk {
background-color: blue; /* Specyficzność: (0,0,1,0) */
}
/* Reguła 2 */
button.anuluj {
background-color: gray; /* Specyficzność: (0,0,1,1) */
}
/* Reguła 3 */
#przycisk-reset {
background-color: red; /* Specyficzność: (0,1,0,0) */
}
W tym przypadku, dla właściwości background-color
przycisku:
- Reguła 3 (
#przycisk-reset
) ma najwyższą specyficzność (0,1,0,0) dzięki selektorowi ID. - Dlatego przycisk będzie miał **czerwone (red)** tło, niezależnie od kolejności reguł w CSS.
Dobre praktyki dotyczące specyficzności
- Utrzymuj niską specyficzność: Staraj się używać jak najmniej specyficznych selektorów, które nadal precyzyjnie wybierają docelowe elementy. Preferuj klasy nad ID do stylizowania.
- Unikaj selektorów ID do stylizowania: Ze względu na ich wysoką specyficzność, utrudniają nadpisywanie stylów.
- Unikaj
!important
: Używaj!important
tylko w ostateczności, np. do nadpisania stylów inline lub stylów z zewnętrznych bibliotek, których nie możesz zmodyfikować. Nadużywanie!important
prowadzi do trudnego w zarządzaniu kodu. - Polegaj na klasach: Klasy oferują dobry balans między specyficznością a możliwością reużycia. Buduj komponenty za pomocą klas.
- Zrozumienie jest kluczem: Dokładne zrozumienie, jak obliczana jest specyficzność, pozwala unikać niespodzianek i pisać bardziej przewidywalny CSS.
Zadania praktyczne
Zadanie 1 (z rozwiązaniem)
Oblicz specyficzność (w notacji A,B,C,D) dla następujących selektorów:
ul li a
.menu > li > a.aktywny:hover
#sidebar input[type="text"]
Rozwiązanie:
ul li a
: (0, 0, 0, 3) - Trzy selektory typu..menu > li > a.aktywny:hover
: (0, 0, 3, 2) - Dwie klasy/pseudoklasy (.menu
,.aktywny
,:hover
) i dwa selektory typu (li
,a
). Kombinatory>
nie liczą się.#sidebar input[type="text"]
: (0, 1, 1, 1) - Jeden ID (#sidebar
), jeden selektor atrybutu ([type="text"]
) i jeden selektor typu (input
).
Zadanie 2 (do samodzielnego wykonania)
Masz poniższy kod HTML i CSS. Jaki kolor będzie miał link wewnątrz diva? Uzasadnij, porównując specyficzność obu reguł.
HTML:
<div class="container">
<a href="#">Kliknij mnie</a>
</div>
CSS:
div a { /* Reguła 1 */
color: blue;
}
.container a { /* Reguła 2 */
color: red;
}
Zadanie 3 (do samodzielnego wykonania)
W kodzie z Zadania 2 zamień miejscami obie reguły CSS. Czy kolor linku się zmieni? Dlaczego?
CSS (zamieniona kolejność):
.container a { /* Reguła 2 */
color: red;
}
div a { /* Reguła 1 */
color: blue;
}
FAQ - Najczęściej zadawane pytania
Czy specyficzność jest jedynym czynnikiem decydującym o priorytecie?
Nie. Specyficzność jest kluczowa, ale działa w ramach szerszego algorytmu kaskady. Najpierw brane jest pod uwagę źródło stylu i ważność (!important
). Dopiero gdy te czynniki są równe dla konkurujących deklaracji, porównywana jest specyficzność selektorów. Jeśli specyficzność również jest równa, decyduje kolejność w kodzie.
Jak obliczyć specyficzność dla selektora z wieloma klasami, np. .klasa1.klasa2
?
Każda klasa w łańcuchu liczy się do części C specyficzności. Selektor .klasa1.klasa2
ma specyficzność (0, 0, 2, 0), ponieważ zawiera dwie klasy. Podobnie div.klasa1.klasa2
miałby specyficzność (0, 0, 2, 1).
Czy istnieją narzędzia do obliczania specyficzności?
Tak, istnieje wiele kalkulatorów specyficzności online (np. Specificity Calculator) oraz wbudowanych narzędzi w edytorach kodu (np. VS Code po najechaniu na selektor), które pomagają szybko określić specyficzność danego selektora.
Co jeśli dwie reguły mają tę samą specyficzność?
Jeśli dwie konkurujące reguły mają dokładnie tę samą specyficzność (identyczne wartości A, B, C i D), to wygrywa ta reguła, która została zdefiniowana **później** w arkuszu stylów (lub w arkuszach dołączonych później w HTML). Kolejność ma znaczenie tylko przy równej specyficzności.