Od arkusza stylów do ekranu: jakie jest miejsce CSS w procesie przeglądarki
Śledź proces implementacji CSS od pobrania do pikseli: jak budowane są DOM, CSSOM i drzewo renderowania, gdzie odbywa się kaskada stylów oraz jakie źródła stylów konkurują o każdy element.
Większość programistów pisze CSS intuicyjnie: zmienia właściwość, przegląda ponownie stronę i sprawdza wynik. To działa, dopóki jakaś zasada nagle odmawia działania, strona pokazuje nieformatowany treść lub „prosta” zmiana stylu powoduje problemy z przewijaniem. Każdy z tych problemów staje się łatwiejszy do zrozumienia, gdy wiemy, co dokładnie robi przeglądarka od chwili otrzymania pliku z stylami do wyświetlenia pikseli.
Ten przewodnik opisuje ten proces na wyższym poziomie. Zobaczysz, jak przeglądarka przekształca HTML w DOM, jak pliki z stylami stają się CSSOM, jak te dwa elementy łączą się w drzewo renderowania, gdzie rozstrzygane są sprzeczne deklaracje stylów oraz które źródła stylów konkurują o każdy element. To również częste pytanie na rozmowie kwalifikacyjnej, zwykle sformułowane jako „jak CSS faktycznie działa?”, a poniższa odpowiedź daje Ci uporządkowany sposób na jej udzielenie.
Pierwszy krok: HTML staje się DOM
Gdy otwierasz adres URL, przeglądarka najpierw otrzymuje dokument HTML. Analizuje ten kod od góry do dołu, tworząc przy tym model obiektów dokumentu. DOM to struktura drzewiasta reprezentująca cały dokument: każdy element jest węzłem, a węzły są ze sobą powiązane jako rodzice, dzieci i rodzeństwo, podobnie jak w drzewie genealogicznym. Wszystko to, co zostało opisane w HTML, znajduje się teraz w tej strukturze, i to właśnie ją JavaScript odczytuje oraz modyfikuje.
Analiza odbywa się stopniowo. Przeglądarka nie czeka na cały plik, zanim zacznie tworzyć węzły, dlatego może odkryć inne zasoby na długo przed zakończeniem pobierania dokumentu.
Krok drugi: pliki stylów stają się CSSOM
Podczas analizy HTML przeglądarka napotyka arkusze stylów, niezależnie od tego, czy są powiązane za pomocą <link rel="stylesheet"> w sekcji head, czy też wplecione w elementy <style>, i zaczyna je również pobierać oraz analizować. CSS jest przekształcany w specjalną strukturę w kształcie drzewa – CSS Object Model, czyli CSSOM. Pełni on tę samą rolę dla stylów, co DOM dla kodu strony.
Przekształcenie CSS w style, które można zastosować do elementu, wymaga więcej pracy niż przekształcenie HTML w węzły. Wyróżniają się tu dwa zadania:
- Rozwiązywanie konfliktów. Liczne deklaracje często odnoszą się do tej samej właściwości w tym samym elemencie. Przeglądarka rozstrzyga te konflikty za pomocą algorytmu zwanego kaskadowaniem.
2em, 50% lub inherit, co jeszcze nie jest wartością, którą silnik układu może wykorzystać. Przeglądarka przekształca je w konkretne wartości.Ściśle mówiąc, CSSOM to zinterpretowana reprezentacja plików stylów, a kaskada i obliczanie wartości następują w momencie, gdy przeglądarka oblicza styl każdego elementu. Dla uproszczonego modelu myślowego można jednak uznać, że „CSS jest interpretowany, konflikty są rozwiązywane, wartości są ustalane i wynik jest przypisywany elementom”.
Jedną z praktycznych konsekwencji jest to, że przeglądarz potrzebuje stylów, zanim będzie mógł wyświetlić cokolwiek zrozumiałego, dlatego pliki ze stylami w bloku head są renderowane dopiero po ich załadowaniu i zinterpretowaniu. Dlatego właśnie duże, wolne pliki ze stylami opóźniają pierwsze wyświetlenie, a małe pliki CSS krytycznego znaczenia są ważne dla wydajności.
Krok trzeci: DOM i CSSOM łączą się w drzewo renderowania
Gdy markup zostanie zinterpretowany i umieszczony w DOM, a style w CSSOM, przeglądarz łączy je w drzewo renderowania. Drzewo to zawiera węzły, które faktycznie zostaną wyświetlone, przy czym każdy z nich jest powiązany ze swoimi obliczonymi stylami. Węzły, które nie generują żadnego wyglądu wizualnego, takie jak zawartość <head> czy elementy z atrybutem display: none, są pomijane.
W tym momencie przeglądarz wie, co ma narysować i jak każda część ma wyglądać pod względem stylów, ale jeszcze nie wie, gdzie co się znajdzie ani jakie będzie to małe.
Krok czwarty: układ i model formatowania wizualnego
Aby przekształcić stylowane węzły w umieszczone pudełka, przeglądarka kieruje się tym, co specyfikacje CSS nazywają modelem formatowania wizualnego. Ta część specyfikacji CSS opisuje, jak elementy drzewa dokumentu są układane na nośnikach wizualnych, takich jak ekran laptopa lub telefonu. Omawia model pudełka, formatowanie blokowe i wierszowe, elementy unoszone, pozycjonowanie oraz inne zasady określające rozmiar i położenie każdego pudełka.
Gdy układ obliczy geometrię każdego pudełka, przeglądarka je rysuje, wypełniając tekstem, kolorami, obramowaniami, obrazami i cieniami, a rezultat w końcu pojawia się na ekranie.
Cały proces w zarysie
Łącząc te etapy otrzymujemy prosty proces przechodzenia od kodu markup do pikseli. Każda strzałka ukrywa znaczną ilość pracy, ale to właśnie kolejność ma znaczenie przy analizie błędów i wydajności:
HTML
↓
DOM
↓
CSS
↓
CSSOM
↓
DOM + CSSOM
↓
Render Tree
↓
Layout
↓
Paint
↓
Pixels on the Screen
Prawdziwe przeglądarki łączą te kroki i dodają kolejne (np. warstwy kompozycji), a zmiana stylu później może zmusić przeglądarkę do ponownego obliczenia stylów, przeprojektowania układu lub ponownego narysowania elementów, w zależności od właściwości. Aby lepiej przyjrzeć się tym kosztom, zapoznaj się z tym, ile kosztuje przeglądarce każda zmiana w CSS.
Dlaczego deklaracje się kolidują
Pozostała część tego przewodnika koncentruje się na pierwszym z dwóch zadań związanych z przetwarzaniem CSS: rozwiązywaniu konfliktów. Algorytmem odpowiedzialnym za to jest kaskada. Łączy on wszystkie arkusze stylów stosowane do dokumentu i, gdy więcej niż jedna deklaracja ustawia tę samą właściwość dla tego samego elementu, decyduje, która z nich ma pierwszeństwo.
Konflikty są nieuniknione, i to nie tylko dlatego, że twój własny arkusz stylów może ustawiać wartość color dla łącza w dwóch miejscach. Stylizacje pochodzą z kilku niezależnych źródeł, nazywanych punktami początkowymi, a wszystkie one są stosowane jednocześnie do tych samych elementów.
Stylizacje autora
Są to deklaracje, które ty i twoja zespół piszecie: wasze arkusze stylów, bloki <style> oraz atrybuty style wewnątrz elementów. Na większości stron stanowią one zdecydowanie największe źródło reguł.
Stylizacje użytkownika
Osoba przeglądająca stronę może również wpływać na style. Przeglądarki umożliwiają użytkownikom dostosowanie ustawień, takich jak domyślna wielkość czcionki, a niektóre obsługują także własne pliki zasad stylów lub rozszerzenia, które je wprowadzają. Preferencje te są szczególnie ważne dla dostępności, ponieważ pozwalają osobom o słabym wzroku lub trudnościach w czytaniu dostosować stronę do swoich potrzeb.
Style agenta użytkownika
Ostatecznie przeglądarka (agent użytkownika) zawiera własny domyślny plik zasad stylów. Dlatego element <a> bez stylizacji wygląda na niebieski i podkreślony, nagłówki są pogrubione i większe od tekstu głównego, a element <body> ma niewielki margines. Te domyślne ustawienia nazywane są stylami agenta użytkownika.
Gdy kaskada łączy wszystkie trzy źródła, ta sama właściwość w tym samym elemencie może łatwo otrzymać kilka sprzecznych wartości, więc przeglądarka potrzebuje deterministycznego sposobu na dokonanie wyboru.
Jak decyduje kaskada
Kaskada porównuje sprzeczne deklaracje przy użyciu ustalonej sekwencji kryteriów, przechodząc do następnego tylko wtedy, gdy poprzednie prowadzi do remisu:
- Pochodzenie i znaczenie. Skąd pochodzi deklaracja oraz czy jest oznaczona
!important. - Specyficzność. Jak dokładnie selektor celuje w element; selektor ID ma pierwszeństwo przed selektorem klasy, który z kolei ma pierwszeństwo przed selektorem typu.
- Kolejność źródeł. Jeśli wszystko inne jest równe, wygrywa deklaracja pojawiająca się później.
Klasyfikacja źródeł
Dla pierwszego kryterium klasyczna kolejność priorytetów od najwyższej do najniższej wygląda następująco:
- Deklaracje użytkownika oznaczone
!important. - Deklaracje autora oznaczone
!important. - Zwykłe deklaracje autora.
Zwróć uwagę, co to oznacza. Twoje zwykłe style przytłaczają standardowe preferencje użytkownika oraz domyślne ustawienia przeglądarki, co właśnie umożliwia projektowanie strony. Jednak !important odwraca kolejność między użytkownikami a autorami: użytkownik, który naprawdę potrzebuje większego rozmiaru czcionki lub wyższego kontrastu, może oznaczyć tę preferencję jako ważną i przezwyciężyć nawet twoje reguły !important. Domyślne ustawienia samej przeglądarki znajdują się na końcu i są stosowane tylko wtedy, gdy nikt inny nic nie określił.
Współczesny CSS udoskonala ten mechanizm. Obecna kaskada uwzględnia również warstwy kaskady (@layer), style ustalane podczas animacji i przejść, a także deklaracje !important w user-agentach, które mają wyższy priorytet niż wszystkie inne ważne deklaracje. Uproszczony powyżej wykaz nadal odzwierciedla najważniejsze relacje w codziennej praktyce; pełny porządek znajduje się w odnośniku do dokumentacji MDN o kaskadzie.
Specyficzność i kolejność źródeł wymagają osobnego, szczegółowego omówienia, w tym tego, jak porównywane są wagi selektorów oraz dlaczego !important tak często powoduje więcej problemów, niż rozwiązuje. Temat ten jest omawiany w artykule jak kaskada wybiera zwycięzcę.
Dlaczego ta wiedza się opłaca
Zrozumienie tego procesu zmienia sposób, w jaki debugujesz i piszesz style:
- Zasady, które nie mają zastosowania, to niemal zawsze straty kaskadowe. Znajomość kolejności pochodzenia, specyficzności oraz porządku źródeł pomaga określić, gdzie szukać rozwiązania, zamiast uciekać się do
!important. - Mgliste wyświetlanie treści bez stylizacji lub z opóźnioną stylizacją wynika z właściwości plików stylowych blokujących renderowanie oraz ze stylów, które pojawiają się po pierwszym renderowaniu.
- Nieprawidłowe interakcje często wynikają ze zmian, które zmuszają do ponownego przetworzenia układu lub rysowania, czemu można zapobiec, znając etap, na którym dana właściwość ma wpływ.
- CSS poddające się utrzymaniu to zazwyczaj CSS o niskiej, przewidywalnej specyficzności oraz jasnym porządku źródeł, co ułatwia przetwarzanie zarówno przeglądarce, jak i kolegom.
Podsumowanie
Przeglądarka przekształca HTML w DOM, arkusze stylów w CSSOM, łączy je w drzewo renderowania składające się z widocznych, ustawionych stylowo węzłów, a następnie wykorzystuje model formatowania wizualnego do układu elementów przed ich narysowaniem. Na etapie CSS pierwszym filarem jest mechanizm kaskadowy: łączy style autora, użytkownika oraz user-agenta i rozwiązuje każdy konflikt według źródła i ważności, a następnie specyficzności oraz kolejności źródeł. Kolejny etap, polegający na przekształceniu wybranych wartości w konkretne liczby, których może użyć silnik układu, jest opisany w tym artykule o tym, jak przeglądarki rozwiązują wartości CSS przed układem.
Literatura pokrewna
- Skąd pochodzą wartości CSS gdy ustawiasz None: Jak działa dziedziczenie — Dowiedz się, jak przeglądarki decydują, czy dana właściwość będzie dziedziczona, dlaczego elementy potomne otrzymują obliczoną wartość rodzica oraz jak funkcje inherit i initial umożliwiają precyzyjną kontrolę.
- Od 66% do 185px: Jak przeglądarki rozwiązuja wartości CSS przed rozmieszczeniem — Prześledź wartość CSS na wszystkich etapach: deklarowanej, kaskadowej, określonej, obliczonej, użytej i faktycznej, oraz zrozum, dlaczego jednostki względne i trik konwersji rem zachowują się w określony sposób.