Dlaczego przeglądarki pasują do selektorów CSS od prawej do lewej?

560

Selektory CSS są dopasowane przez silniki przeglądarki od prawej do lewej. Najpierw znajdują dzieci, a następnie sprawdzają rodziców, czy pasują do reszty reguł.

  1. Dlaczego to?
  2. Czy to tylko dlatego, że specyfikacja mówi?
  3. Czy wpływa to na ostateczny układ, jeśli był oceniany od lewej do prawej?

Dla mnie najprostszym sposobem na to byłoby użycie selektorów z najmniejszą liczbą elementów. Najpierw identyfikatory (ponieważ powinny zwracać tylko 1 element). Wtedy może klasy lub element, który ma najmniejszą liczbę węzłów - np. Może istnieć tylko jeden zakres na stronie, więc przejdź bezpośrednio do tego węzła z dowolną regułą, która odwołuje się do zakresu.

Oto kilka linków potwierdzających moje roszczenia

  1. http://code.google.com/speed/page-speed/docs/rendering.html
  2. https://developer.mozilla.org/en/Writing_Efficient_CSS

Wygląda na to, że robi się to w ten sposób, aby uniknąć konieczności patrzenia na wszystkie dzieci rodzica (które mogą być wielu), a nie na wszystkich rodziców dziecka, które musi być jednym. Nawet jeśli DOM jest głęboki, sprawdziłby tylko jeden węzeł na poziom zamiast wielu w dopasowaniu RTL. Czy łatwiej / szybciej jest oceniać selektory CSS LTR lub RTL?

tgandrews
źródło
5
3. Nie - bez względu na to, jak to czytasz, selektor zawsze pasuje do tego samego zestawu elementów.
Šime Vidas,
39
Co do wartości, przeglądarka nie może założyć, że twoje identyfikatory są unikalne. Możesz umieścić ten sam id = "foo" w całym DOM, a #fooselektor będzie musiał dopasować wszystkie te węzły. jQuery ma opcję powiedzenia, że ​​$ („# foo”) zawsze zwróci tylko jeden element, ponieważ definiują one własne API z własnymi regułami. Ale przeglądarki muszą implementować CSS, a CSS mówi, aby dopasować wszystko w dokumencie do podanego identyfikatora.
Boris Zbarsky
4
@Quentin W „niezgodnym” (do HTML) identyfikatorze dokumentu może być nieunikalny, aw tych dokumentach CSS wymaga dopasowania wszystkich elementów o tym identyfikatorze. Sam CSS nie nakłada żadnych wymagań normatywnych na unikalność identyfikatorów; cytowany tekst ma charakter informacyjny.
Boris Zbarsky
5
@Boris Zbarsky To, co robi jQuery, zależy od ścieżki kodu w jQuery. W niektórych przypadkach jQuery używa NodeSelector API (natywny querySelectorAll). W innych przypadkach stosuje się Sizzle. Sizzle nie pasuje do wielu identyfikatorów, ale QSA tak (AYK). Wybrana ścieżka zależy od selektora, kontekstu oraz przeglądarki i jej wersji. Interfejs API zapytań jQuery korzysta z tego, co nazwałem „Najpierw natywnym, podwójnym podejściem”. Napisałem o tym artykuł, ale nie działa. Chociaż można tu znaleźć: fortybelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett
5
Nie jestem nawet pewien, czy ktokolwiek oprócz was obojga może zrozumieć, co się dzieje z tą długą rozmową. Mamy czat dla tych rozszerzonych dyskusji. Wszystko, co naprawdę chcesz zatrzymać, powinno zostać postawione w odpowiedzi na pytanie lub odpowiedź, szczególnie jeśli zawierają wyjaśnienia. Przepełnienie stosu nie obsługuje dobrze dyskusji w komentarzach.
George Stocker

Odpowiedzi:

824

Pamiętaj, że gdy przeglądarka dopasowuje selektor, ma jeden element (ten, dla którego próbuje określić styl) oraz wszystkie reguły i ich selektory i musi znaleźć, które reguły pasują do elementu. To różni się od zwykłej rzeczy jQuery, powiedzmy, gdzie masz tylko jeden selektor i musisz znaleźć wszystkie elementy, które pasują do tego selektora.

Jeśli miałeś tylko jeden selektor i tylko jeden element do porównania z tym selektorem, wtedy od lewej do prawej ma sens w niektórych przypadkach. Ale to zdecydowanie nie jest sytuacja przeglądarki. Przeglądarka próbuje wyrenderować Gmaila lub cokolwiek innego i ma ten <span>, który próbuje nadać styl, a ponad 10 000 reguł Gmail umieszcza w arkuszu stylów (nie liczę tego).

W szczególności w sytuacji, gdy przeglądarka patrzy na większość selektorów, które jej zdaniem nie pasują do danego elementu. Problemem staje się więc decyzja, że ​​selektor nie pasuje tak szybko, jak to możliwe; jeśli to wymaga trochę dodatkowej pracy w przypadkach, które pasują, nadal wygrywasz z powodu całej pracy, którą oszczędzasz w przypadkach, które się nie zgadzają.

Jeśli zaczniesz od dopasowania najbardziej prawej części selektora do elementu, prawdopodobnie nie będzie pasować i gotowe. Jeśli to pasuje, musisz wykonać więcej pracy, ale tylko proporcjonalnie do głębokości drzewa, która w większości przypadków nie jest tak duża.

Z drugiej strony, jeśli zaczniesz od dopasowania lewej skrajnej części selektora ... z czym to dopasujesz? Musisz zacząć chodzić po DOM, szukając węzłów, które mogą do niego pasować. Samo odkrycie, że nic nie pasuje do tej lewej części, może chwilę potrwać.

Tak więc przeglądarki pasują do siebie z prawej strony; daje oczywisty punkt wyjścia i pozwala bardzo szybko pozbyć się większości kandydatów na selektorów. Niektóre dane można zobaczyć na stronie http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (choć notacja jest myląca), ale wynik jest szczególnie w przypadku Gmaila dwa lata temu dla 70% par (reguła, element) można było stwierdzić, że reguła nie pasuje, po zbadaniu części znacznika / klasy / identyfikatora najbardziej wysuniętego w prawo selektora reguły. Odpowiednia liczba dla zestawu testów wydajności pobierania Mozilli wyniosła 72%. Dlatego naprawdę warto spróbować jak najszybciej pozbyć się 2/3 wszystkich zasad, a potem martwić się o dopasowanie pozostałej 1/3.

Zauważ też, że istnieją już inne optymalizacje przeglądarek, aby uniknąć nawet próby dopasowania reguł, które na pewno nie będą pasować. Na przykład, jeśli skrajny prawy selektor ma identyfikator, a ten identyfikator nie pasuje do identyfikatora elementu, wówczas w Gecko nie będzie próby dopasowania tego selektora do tego elementu: zestaw „selektorów z identyfikatorami”, które są podejmowane pochodzi z wyszukiwania hashtable identyfikatora elementu. Jest to więc 70% reguł, które mają całkiem spore szanse na dopasowanie, które wciąż nie pasują po rozważeniu tylko tagu / klasy / id najbardziej wysuniętego w prawo selektora.

Boris Zbarsky
źródło
5
Jako mały bonus, sensowniejsze jest czytanie go w języku RTL niż LTR, nawet w języku angielskim. Przykład: stackoverflow.com/questions/3851635/css-combinator-precedence/…
BoltClock
6
Pamiętaj, że dopasowanie RTL dotyczy tylko kombinatorów . Nie przechodzi do prostego poziomu selektora. Oznacza to, że przeglądarka pobiera skrajnie prawy selektor złożony lub sekwencję prostych selektorów i próbuje dopasować go atomowo. Następnie, jeśli jest dopasowanie, podąża za kombinatorem w lewo do następnego selektora złożonego i sprawdza element w tej pozycji i tak dalej. Nie ma dowodów, że przeglądarka odczytuje każdą część RTL selektora związków; w rzeczywistości ostatni akapit pokazuje dokładnie inaczej (kontrole id zawsze są najważniejsze).
BoltClock
5
Właściwie, zanim dopasujesz selektory, przynajmniej w Gecko, zmienna i przestrzeń nazw są na pierwszym miejscu. Identyfikator (podobnie jak zmienna i nazwy klas) jest rozpatrywany na etapie wstępnego filtrowania, który eliminuje większość reguł bez próby dopasowania selektorów.
Boris Zbarsky
To może pomóc w zależności od tego, jakie optymalizacje robi UA, ale nie pomoże krokowi wstępnego filtrowania w Gecko, który opisałem powyżej. Istnieje jednak drugi etap filtrowania, który działa na identyfikatorach i klasach używanych w kombinatorach potomnych, ale może to pomóc.
Boris Zbarsky
@Benito Ciaro: Nie wspominając o problemach ze specyfiką.
BoltClock
33

Analiza od prawej do lewej, nazywana również analizą od dołu do góry, jest w rzeczywistości wydajna dla przeglądarki.

Rozważ następujące:

#menu ul li a { color: #00f; }

Przeglądarka najpierw sprawdza a, a lipotem ul, a potem #menu.

Jest tak, ponieważ przeglądarka skanuje stronę, musi tylko spojrzeć na bieżący element / węzeł i wszystkie poprzednie węzły / elementy, które przeskanował.

Należy zauważyć, że przeglądarka rozpoczyna przetwarzanie w momencie, gdy otrzymuje pełny tag / węzeł i nie musi czekać na całą stronę, z wyjątkiem momentu znalezienia skryptu, w którym to przypadku tymczasowo zatrzymuje się i kończy wykonywanie skryptu, a następnie idzie do przodu.

Jeśli zrobi to na odwrót, będzie nieefektywne, ponieważ przeglądarka znalazła element, który skanował przy pierwszym sprawdzeniu, ale została zmuszona do dalszego przeglądania dokumentu pod kątem wszystkich dodatkowych selektorów. W tym celu przeglądarka musi mieć cały HTML i może wymagać zeskanowania całej strony przed rozpoczęciem malowania css.

Jest to sprzeczne z tym, jak większość bibliotek parsuje dom. Tam dom jest zbudowany i nie trzeba skanować całej strony, po prostu znajdź pierwszy element, a następnie dopasowuj pozostałe w nim.

aWebDeveloper
źródło
20

Pozwala na kaskadowanie od bardziej szczegółowych do mniej szczegółowych. Pozwala również na zwarcie w aplikacji. Jeśli bardziej szczegółowa reguła ma zastosowanie we wszystkich aspektach, których dotyczy reguła nadrzędna, wszystkie reguły nadrzędne są ignorowane. Jeśli w elemencie macierzystym znajdują się inne bity, są one stosowane.

Gdybyś odwrócił się, sformatowałbyś według rodzica, a następnie nadpisał za każdym razem, gdy dziecko ma coś innego. Na dłuższą metę jest to o wiele więcej pracy niż ignorowanie pozycji w regułach, które są już zajęte.

Gregory A Beamer
źródło
11
To osobny problem. Kaskadowanie wykonuje się, sortując reguły według specyficzności, a następnie dopasowując je w kolejności szczegółowości. Ale pytanie tutaj brzmi: dlaczego dla danej reguły dopasowujesz jej selektory w określony sposób.
Boris Zbarsky