Warum stimmen Browser mit CSS-Selektoren von rechts nach links überein?

560

CSS-Selektoren werden von Browser-Engines von rechts nach links abgeglichen. Also finden sie zuerst die Kinder und überprüfen dann ihre Eltern, um festzustellen, ob sie mit den übrigen Teilen der Regel übereinstimmen.

  1. Warum ist das?
  2. Ist es nur, weil die Spezifikation sagt?
  3. Beeinflusst es das eventuelle Layout, wenn es von links nach rechts ausgewertet wurde?

Für mich wäre der einfachste Weg, dies zu tun, die Selektoren mit der geringsten Anzahl von Elementen zu verwenden. Also zuerst IDs (da sie nur 1 Element zurückgeben sollten). Dann vielleicht Klassen oder ein Element mit der geringsten Anzahl von Knoten - z. B. gibt es möglicherweise nur einen Bereich auf der Seite. Gehen Sie also mit einer Regel, die auf einen Bereich verweist, direkt zu diesem Knoten.

Hier sind einige Links, die meine Behauptungen stützen

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

Es klingt so, als würde dies auf diese Weise geschehen, um zu vermeiden, dass alle Kinder der Eltern (die viele sein könnten) und nicht alle Eltern eines Kindes, das eins sein muss, betrachtet werden müssen. Selbst wenn das DOM tief ist, wird beim RTL-Abgleich nur ein Knoten pro Ebene und nicht mehrere Knoten betrachtet. Ist es einfacher / schneller, CSS-Selektoren LTR oder RTL zu bewerten?

tgandrews
quelle
5
3. Nein - egal wie Sie es lesen, der Selektor entspricht immer dem gleichen Satz von Elementen.
Šime Vidas
39
Ein Browser kann nicht davon ausgehen, dass Ihre IDs eindeutig sind. Sie könnten dieselbe ID = "foo" in Ihrem gesamten DOM verwenden, und ein #fooSelektor müsste mit all diesen Knoten übereinstimmen. jQuery hat die Möglichkeit zu sagen, dass $ ("# foo") immer nur ein Element zurückgibt, da sie ihre eigene API mit eigenen Regeln definieren. Browser müssen jedoch CSS implementieren, und CSS sagt, dass alles im Dokument mit der angegebenen ID abgeglichen werden soll.
Boris Zbarsky
4
@Quentin In einem "nicht konformen" (zu HTML) Dokument können IDs nicht eindeutig sein, und in diesen Dokumenten erfordert CSS, dass alle Elemente mit dieser ID abgeglichen werden. CSS selbst stellt keine normativen Anforderungen an eindeutige IDs. Der von Ihnen zitierte Text ist informativ.
Boris Zbarsky
5
@Boris Zbarsky Was jQuery macht, hängt vom Codepfad in jQuery ab. In einigen Fällen verwendet jQuery die NodeSelector-API (nativ querySelectorAll). In anderen Fällen wird Sizzle verwendet. Sizzle stimmt nicht mit mehreren IDs überein, QSA jedoch (AYK). Der eingeschlagene Pfad hängt von der Auswahl, dem Kontext, dem Browser und seiner Version ab. Die Abfrage-API von jQuery verwendet das, was ich als "Native First, Dual Approach" bezeichnet habe. Ich habe einen Artikel darüber geschrieben, aber er ist ausgefallen. Obwohl Sie hier finden können: vierzigbelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett
5
Ich bin mir nicht mal sicher, ob jemand außer euch beiden herausfinden kann, was mit diesem langen Gespräch los ist. Wir haben Chat für diese erweiterten Diskussionen. Alles, was Sie wirklich behalten möchten, sollte in die Frage oder Antwort aufgenommen werden, insbesondere wenn es sich um klarstellende Informationen handelt. Der Stapelüberlauf verarbeitet Diskussionen in Kommentaren nicht gut.
George Stocker

Antworten:

824

Denken Sie daran, dass ein Browser beim Selektorabgleich ein Element (dasjenige, für das er den Stil bestimmen möchte) und alle Ihre Regeln und deren Selektoren enthält und herausfinden muss, welche Regeln mit dem Element übereinstimmen. Dies unterscheidet sich beispielsweise von der üblichen jQuery-Funktion, bei der Sie nur einen Selektor haben und alle Elemente finden müssen, die diesem Selektor entsprechen.

Wenn Sie nur einen Selektor und nur ein Element hatten, um ihn mit diesem Selektor zu vergleichen, ist von links nach rechts in einigen Fällen sinnvoller. Aber das ist definitiv nicht die Situation des Browsers. Der Browser versucht, Google Mail oder was auch immer zu rendern, und hat diejenige <span>, die er zu stylen versucht, und die über 10.000 Regeln, die Google Mail in sein Stylesheet einfügt (ich erfinde diese Zahl nicht).

Insbesondere in der Situation, in der der Browser die meisten Selektoren betrachtet, die er in Betracht zieht, stimmen sie nicht mit dem betreffenden Element überein. Das Problem besteht also darin, zu entscheiden, dass ein Selektor nicht so schnell wie möglich übereinstimmt. Wenn dies in den übereinstimmenden Fällen ein wenig zusätzliche Arbeit erfordert, gewinnen Sie trotzdem aufgrund der gesamten Arbeit, die Sie in den nicht übereinstimmenden Fällen sparen.

Wenn Sie zunächst nur den am weitesten rechts stehenden Teil des Selektors mit Ihrem Element abgleichen, stimmt er wahrscheinlich nicht überein, und Sie sind fertig. Wenn es passt, müssen Sie mehr arbeiten, aber nur proportional zu Ihrer Baumtiefe, die in den meisten Fällen nicht so groß ist.

Auf der anderen Seite, wenn Sie damit beginnen, den am weitesten links liegenden Teil des Selektors abzugleichen ... gegen was passen Sie ihn an? Sie müssen das DOM durchsuchen und nach Knoten suchen, die möglicherweise zu ihm passen. Nur zu entdecken, dass es nichts gibt, das zu diesem Teil ganz links passt, kann eine Weile dauern.

Die Browser stimmen also von rechts überein. Es gibt einen offensichtlichen Ausgangspunkt und ermöglicht es Ihnen, die meisten Kandidaten-Selektoren sehr schnell loszuwerden. Sie können einige Daten unter http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 sehen (obwohl die Notation verwirrend ist), aber das Ergebnis ist das insbesondere für Google Mail Vor zwei Jahren konnten Sie für 70% der (Regel-, Element-) Paare entscheiden, dass die Regel nicht übereinstimmt, nachdem Sie nur die Teile tag / class / id des Selektors ganz rechts für die Regel untersucht haben. Die entsprechende Anzahl für Mozillas Pageload-Leistungstestsuite betrug 72%. Es lohnt sich also wirklich, diese 2/3 aller Regeln so schnell wie möglich loszuwerden und sich dann nur darum zu kümmern, dass die verbleibenden 1/3 übereinstimmen.

Beachten Sie auch, dass es bereits andere Optimierungen gibt, die Browser durchführen, um zu vermeiden, dass versucht wird, Regeln abzugleichen, die definitiv nicht übereinstimmen. Wenn beispielsweise der Selektor ganz rechts eine ID hat und diese ID nicht mit der ID des Elements übereinstimmt, wird in Gecko nicht versucht, diesen Selektor mit diesem Element abzugleichen: die Menge der "Selektoren mit IDs", die versucht werden stammt aus einer Hashtabellensuche in der ID des Elements. Dies sind also 70% der Regeln, bei denen die Wahrscheinlichkeit einer Übereinstimmung ziemlich gut ist und die immer noch nicht übereinstimmen, wenn nur das Tag / die Klasse / die ID des Selektors ganz rechts berücksichtigt wird.

Boris Zbarsky
quelle
5
Als kleinen Bonus ist es sinnvoller, RTL als LTR auch auf Englisch zu lesen. Ein Beispiel: stackoverflow.com/questions/3851635/css-combinator-precedence/…
BoltClock
6
Beachten Sie, dass der RTL-Abgleich nur für Kombinatoren gilt . Es wird kein Drilldown auf die einfache Auswahlstufe durchgeführt. Das heißt, ein Browser verwendet den zusammengesetzten Selektor ganz rechts oder eine Folge einfacher Selektoren und versucht, ihn atomar abzugleichen. Wenn es eine Übereinstimmung gibt, folgt sie dem Kombinator nach links zum nächsten zusammengesetzten Selektor und überprüft das Element an dieser Position und so weiter. Es gibt keine Hinweise darauf, dass ein Browser jeden Teil einer zusammengesetzten Selektor-RTL liest. Tatsächlich zeigt der letzte Absatz genau etwas anderes (ID-Prüfungen stehen immer an erster Stelle).
BoltClock
5
Wenn Sie zumindest in Gecko mit Selektoren übereinstimmen, stehen der Tagname und der Namespace an erster Stelle. Die ID (sowie der Tagname und die Klassennamen) werden in einem Vorfilterungsschritt berücksichtigt, der die meisten Regeln eliminiert, ohne wirklich zu versuchen, mit den Selektoren übereinzustimmen.
Boris Zbarsky
Dies kann hilfreich sein, je nachdem, welche Optimierungen die UA vornimmt, aber es hilft nicht beim Vorfilterungsschritt in Gecko, den ich oben beschrieben habe. Es gibt einen zweiten Filterschritt, der für IDs und Klassen funktioniert, die nur für Nachkommen-Kombinatoren verwendet werden, damit dies jedoch hilfreich sein kann.
Boris Zbarsky
@ Benito Ciaro: Ganz zu schweigen von Spezifitätsproblemen.
BoltClock
33

Das Parsen von rechts nach links, auch als Bottom-up-Parsing bezeichnet, ist für den Browser tatsächlich effizient.

Folgendes berücksichtigen:

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

Der Browser sucht zuerst nach a, dann li, dann ulund dann #menu.

Dies liegt daran, dass der Browser beim Scannen der Seite nur das aktuelle Element / den aktuellen Knoten und alle zuvor gescannten Knoten / Elemente anzeigen muss.

Zu beachten ist, dass der Browser mit der Verarbeitung beginnt, sobald er ein vollständiges Tag / einen vollständigen Knoten erhält, und nicht auf die gesamte Seite warten muss, außer wenn er ein Skript findet. In diesem Fall wird die Ausführung des Skripts vorübergehend angehalten und abgeschlossen geht vorwärts.

Wenn es umgekehrt ist, ist es ineffizient, da der Browser das Element gefunden hat, das er bei der ersten Prüfung gescannt hat, dann aber gezwungen war, das Dokument weiter nach allen zusätzlichen Selektoren zu durchsuchen. Dazu muss der Browser über das gesamte HTML verfügen und möglicherweise die gesamte Seite scannen, bevor mit dem CSS-Malen begonnen wird.

Dies steht im Widerspruch dazu, wie die meisten Bibliotheken dom analysieren. Dort wird der Dom erstellt und es muss nicht die gesamte Seite gescannt werden, sondern nur das erste Element gefunden und dann andere darin abgeglichen werden.

aWebDeveloper
quelle
20

Es ermöglicht die Kaskadierung von spezifischer zu weniger spezifisch. Es ermöglicht auch einen Kurzschluss in der Anwendung. Wenn die spezifischere Regel in allen Aspekten gilt, für die die übergeordnete Regel gilt, werden alle übergeordneten Regeln ignoriert. Wenn das übergeordnete Element andere Bits enthält, werden diese angewendet.

Wenn Sie umgekehrt vorgehen würden, würden Sie nach Eltern formatieren und dann jedes Mal überschreiben, wenn das Kind etwas anderes hat. Auf lange Sicht ist dies viel mehr Arbeit als das Ignorieren von Elementen in Regeln, die bereits erledigt sind.

Gregory A Beamer
quelle
11
Das ist ein separates Thema. Sie führen die Kaskadierung durch, indem Sie die Regeln nach Spezifität sortieren und dann in der Spezifitätsreihenfolge mit ihnen abgleichen. Die Frage hier ist jedoch, warum Sie für eine bestimmte Regel auf bestimmte Weise mit ihren Selektoren übereinstimmen.
Boris Zbarsky