Koszt selektorów i best practices

Koszt selektorów i best practices

W CSS nie wszystkie selektory są sobie równe. Choć efekt wizualny może być identyczny, niektóre selektory są znacznie „droższe” dla przeglądarki pod względem wydajności. W tej lekcji nauczysz się, jak świadomie tworzyć selektory, które są szybkie, przewidywalne i łatwe w utrzymaniu.

Jak przeglądarka „czyta” CSS?

Pod maską przeglądarka przetwarza CSS w trzech etapach:

  1. Parsowanie selektorów – przeglądarka rozbija CSS na reguły i sprawdza, które z nich pasują do elementów DOM.
  2. Matchowanie – selektory są porównywane z drzewem DOM – od prawej do lewej (ważne!).
  3. Renderowanie – po ustaleniu stylów, przeglądarka rysuje efekt na ekranie.

Im bardziej złożony selektor, tym więcej pracy przeglądarki.

Przykład: selektor prosty vs złożony
/* Prosty */
.button {
  background-color: blue;
}

/* Złożony */
section > div.container ul li a.button:hover {
  background-color: blue;
}

Efekt ten sam, ale przeglądarka w drugim przypadku musi wykonać więcej kroków, aby „dojść” do odpowiedniego elementu.

Najdroższe selektory (wydajnościowo)
  • * – gwiazdka (selektor uniwersalny)
  • li a – selektory potomków bez klasy
  • div > a.button:hover – złożone z potomkami, pseudoklasami
  • [attribute=value] – selektory atrybutów
  • :not(.klasa), :nth-child – pseudoklasy z logiką

Uwaga: różnice w wydajności są marginalne przy małych projektach, ale w dużych aplikacjach SPA – mają znaczenie.

Best practices – dobre praktyki projektowania selektorów
  1. Unikaj zbyt ogólnych selektorów – np. *, div p, ul li a
  2. Preferuj klasy zamiast selektorów tagów.card-title zamiast h3.card-title
  3. Nie zagnieżdżaj zbyt głęboko – maksymalnie 2–3 poziomy, np. .section .card .title
  4. Unikaj selektorów ID – mają najwyższą specyficzność, trudne do nadpisywania
  5. Stosuj zasadę: selektor = nazwa roli komponentu – np. .form__label
Specyficzność – co wpływa na to, kto „wygrywa”?

Przeglądarka stosuje reguły na podstawie tzw. specyficzności. W skrócie:

  • #id = 100 punktów
  • .class, [attr], :hover = 10 punktów
  • div, h1, p = 1 punkt

Im wyższa specyficzność, tym trudniej coś nadpisać. Dlatego np. #header h1 przeważy nad .header__title.

Złe praktyki (których warto unikać)
/* Zła praktyka: selektor z ID + tag + klasa + pseudoklasa */
#main-content div.card .title:hover {
  color: red;
}

To trudne do debugowania, trudne do nadpisania, kosztowne przy renderze. Lepiej:

.card__title:hover {
  color: red;
}
Jakie selektory są „tanie” i wydajne?
  • Proste klasy komponentów: .button, .alert, .nav__item
  • Klasy z BEM: .form__input, .card--highlighted
  • Klasy narzędziowe: .text-center, .mb-2
  • Ograniczona głębokość: maks. 2–3 selektory w łańcuchu
Ćwiczenie

Zidentyfikuj 3 reguły CSS w swoim projekcie, które mają selektory z ponad 3 poziomami zagnieżdżenia lub używają ID. Przepisz je w stylu komponentowym (np. BEM) tak, by:

  • każdy komponent miał unikalną klasę,
  • selekcja była maksymalnie uproszczona,
  • kod był łatwy do nadpisania i rozbudowy.
Wniosek

CSS jest szybki – ale tylko wtedy, gdy Ty mu na to pozwolisz. Zbyt skomplikowane selektory mogą powodować trudne do wykrycia problemy wydajnościowe. Prosta zasada: preferuj klasy, trzymaj się płytkich struktur, nie buduj selektorów jak drzewo genealogiczne. To czyni Twój kod przewidywalnym, skalowalnym i przyjaznym dla innych frontendowców.