Pourquoi les navigateurs correspondent-ils aux sélecteurs CSS de droite à gauche?

560

Les sélecteurs CSS correspondent aux moteurs de navigation de droite à gauche. Ils trouvent donc d'abord les enfants, puis vérifient leurs parents pour voir s'ils correspondent aux autres parties de la règle.

  1. Pourquoi est-ce?
  2. Est-ce juste parce que la spécification le dit?
  3. Cela affecte-t-il la mise en page éventuelle si elle a été évaluée de gauche à droite?

Pour moi, la façon la plus simple de le faire serait d'utiliser les sélecteurs avec le moins d'éléments. Donc les identifiants en premier (car ils ne doivent renvoyer qu'un seul élément). Alors peut-être des classes ou un élément qui a le moins de nœuds - par exemple, il ne peut y avoir qu'une seule étendue sur la page, alors allez directement à ce nœud avec n'importe quelle règle qui fait référence à une étendue.

Voici quelques liens sauvegardant mes réclamations

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

On dirait que c'est fait de cette façon pour éviter d'avoir à regarder tous les enfants du parent (qui pourraient être nombreux) plutôt que tous les parents d'un enfant qui doit être un. Même si le DOM est profond, il ne regarderait qu'un nœud par niveau plutôt que plusieurs dans la correspondance RTL. Est-il plus facile / plus rapide d'évaluer les sélecteurs CSS LTR ou RTL?

tgandrews
la source
5
3. Non - peu importe comment vous le lisez, le sélecteur correspond toujours au même ensemble d'éléments.
Šime Vidas
39
Pour ce que ça vaut, un navigateur ne peut pas supposer que vos identifiants sont uniques. Vous pouvez coller le même id = "foo" sur tout votre DOM, et un #foosélecteur devrait correspondre à tous ces nœuds. jQuery a la possibilité de dire que $ ("# foo") retournera toujours un seul élément, car ils définissent leur propre API avec ses propres règles. Mais les navigateurs doivent implémenter CSS, et CSS dit de faire correspondre tout ce qui se trouve dans le document avec l'ID donné.
Boris Zbarsky
4
@Quentin Dans un document "non conforme" (en HTML), les ID de document peuvent être non uniques et, dans ces documents, CSS nécessite de faire correspondre tous les éléments avec cet ID. Le CSS lui-même n'impose aucune exigence normative à ce que les ID soient uniques; le texte que vous citez est informatif.
Boris Zbarsky
5
@Boris Zbarsky L'action de jQuery dépend du chemin du code dans jQuery. Dans certains cas, jQuery utilise l'API NodeSelector (native querySelectorAll). Dans d'autres cas, Sizzle est utilisé. Sizzle ne correspond pas à plusieurs ID, mais QSA le fait (AYK). Le chemin emprunté dépend du sélecteur, du contexte, du navigateur et de sa version. L'API Query de jQuery utilise ce que j'ai appelé "Native First, Dual Approach". J'ai écrit un article là-dessus, mais c'est en baisse. Bien que vous puissiez trouver ici: quarantebelow.ca/hosted/dhtmlkitchen/JavaScript-Query-Engines.html
Garrett
5
Je ne suis même sûr de personne, mais vous pouvez comprendre ce qui se passe avec cette longue conversation. Nous avons un chat pour ces discussions prolongées. Tout ce que vous voulez vraiment garder devrait être mis dans la question ou une réponse, surtout s'il s'agit de clarifier des informations. Stack Overflow ne gère pas bien la discussion dans les commentaires.
George Stocker

Réponses:

824

Gardez à l'esprit que lorsqu'un navigateur effectue une correspondance de sélecteur, il a un élément (celui pour lequel il essaie de déterminer le style) et toutes vos règles et leurs sélecteurs et il doit trouver les règles correspondant à l'élément. C'est différent de la chose jQuery habituelle, disons, où vous n'avez qu'un seul sélecteur et vous devez trouver tous les éléments qui correspondent à ce sélecteur.

Si vous n'aviez qu'un seul sélecteur et un seul élément à comparer à ce sélecteur, alors de gauche à droite est plus logique dans certains cas. Mais ce n'est décidément pas la situation du navigateur. Le navigateur essaie de rendre Gmail ou autre chose et a celui <span>qu'il essaie de styliser et les 10 000+ règles que Gmail met dans sa feuille de style (je n'invente pas ce nombre).

En particulier, dans la situation où le navigateur regarde la plupart des sélecteurs qu'il considère ne correspondent pas à l'élément en question. Le problème devient donc de décider qu'un sélecteur ne correspond pas aussi vite que possible; si cela nécessite un peu de travail supplémentaire dans les cas qui correspondent, vous gagnez encore en raison de tout le travail que vous enregistrez dans les cas qui ne correspondent pas.

Si vous commencez simplement par faire correspondre la partie la plus à droite du sélecteur avec votre élément, alors il y a des chances qu'il ne corresponde pas et vous avez terminé. Si cela correspond, vous devez faire plus de travail, mais seulement proportionnellement à la profondeur de votre arbre, qui n'est pas si grande dans la plupart des cas.

D'un autre côté, si vous commencez par faire correspondre la partie la plus à gauche du sélecteur ... à quoi la comparez-vous? Vous devez commencer à parcourir le DOM, à la recherche de nœuds qui pourraient lui correspondre. Découvrir que rien ne correspond à la partie la plus à gauche peut prendre un certain temps.

Les navigateurs correspondent donc à droite; il donne un point de départ évident et vous permet de vous débarrasser de la plupart des candidats sélecteurs très rapidement. Vous pouvez voir certaines données sur http://groups.google.com/group/mozilla.dev.tech.layout/browse_thread/thread/b185e455a0b3562a/7db34de545c17665 (bien que la notation soit déroutante), mais le résultat est celui de Gmail en particulier il y a deux ans, pour 70% des paires (règle, élément), vous pouviez décider que la règle ne correspond pas après avoir simplement examiné les parties tag / classe / id du sélecteur le plus à droite pour la règle. Le nombre correspondant pour la suite de tests de performances de chargement de page de Mozilla était de 72%. Il vaut donc vraiment la peine d'essayer de se débarrasser de ces 2/3 de toutes les règles aussi vite que possible et de ne s'inquiéter que de faire correspondre le tiers restant.

Notez également qu'il existe déjà d'autres optimisations que les navigateurs font pour éviter d'essayer de faire correspondre des règles qui ne correspondront certainement pas. Par exemple, si le sélecteur le plus à droite a un identifiant et que cet identifiant ne correspond pas à l'identifiant de l'élément, alors il n'y aura aucune tentative de faire correspondre ce sélecteur à cet élément du tout dans Gecko: l'ensemble des "sélecteurs avec identifiants" qui sont tentés provient d'une recherche de table de hachage sur l'ID de l'élément. C'est donc 70% des règles qui ont de très bonnes chances de correspondre qui ne correspondent toujours pas après avoir considéré uniquement la balise / classe / id du sélecteur le plus à droite.

Boris Zbarsky
la source
5
En prime, il est plus logique de le lire en RTL qu'en LTR, même en anglais. Un exemple: stackoverflow.com/questions/3851635/css-combinator-precedence/…
BoltClock
6
Notez que la correspondance RTL s'applique uniquement aux combinateurs . Il ne descend pas au niveau du sélecteur simple. Autrement dit, un navigateur prend le sélecteur composé le plus à droite , ou une séquence de sélecteurs simples, et tente de le faire correspondre atomiquement. Ensuite, s'il y a correspondance, il suit le combinateur vers la gauche jusqu'au prochain sélecteur composé et vérifie l'élément dans cette position, et ainsi de suite. Il n'y a aucune preuve qu'un navigateur lit chaque partie d'un sélecteur composé RTL; en fait, le dernier paragraphe montre précisément le contraire (les vérifications d'identité viennent toujours en premier).
BoltClock
5
En fait, au moment où vous faites correspondre les sélecteurs, au moins dans Gecko, la variable et l'espace de noms viennent en premier. L'identifiant (ainsi que la variable et les noms de classe) sont considérés dans une étape de pré-filtrage qui élimine la plupart des règles sans vraiment essayer de faire correspondre les sélecteurs.
Boris Zbarsky
Cela pourrait aider en fonction des optimisations effectuées par l'agent utilisateur, mais cela n'aidera pas l'étape de préfiltrage dans Gecko que je décris ci-dessus. Cependant, il existe une deuxième étape de filtrage qui fonctionne sur les ID et les classes qui est utilisée pour les combinateurs descendants uniquement.
Boris Zbarsky
@Benito Ciaro: Sans parler des problèmes de spécificité également.
BoltClock
33

L'analyse de droite à gauche, également appelée analyse ascendante, est en fait efficace pour le navigateur.

Considérer ce qui suit:

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

Le navigateur vérifie d'abord a, puis li, puis ul, puis #menu.

En effet, lorsque le navigateur numérise la page, il doit simplement regarder l'élément / nœud actuel et tous les nœuds / éléments précédents qu'il a analysés.

La chose à noter est que le navigateur commence le traitement au moment où il obtient une balise / un nœud complet et n'a pas besoin d'attendre la page entière, sauf lorsqu'il trouve un script, auquel cas il suspend temporairement et termine l'exécution du script, puis va de l'avant.

S'il fait l'inverse, il sera inefficace car le navigateur a trouvé l'élément qu'il analysait lors de la première vérification, mais il a ensuite été obligé de continuer à parcourir le document pour tous les sélecteurs supplémentaires. Pour cela, le navigateur doit avoir l'intégralité du code HTML et peut avoir besoin de numériser la page entière avant de commencer la peinture CSS.

Ceci est contraire à la façon dont la plupart des bibliothèques analysent dom. Là, le dom est construit et il n'a pas besoin de parcourir toute la page, il suffit de trouver le premier élément et de continuer à faire correspondre les autres à l'intérieur.

aWebDeveloper
la source
20

Il permet de passer du plus spécifique au moins spécifique. Il permet également un court-circuit dans l'application. Si la règle plus spécifique s'applique dans tous les aspects auxquels s'applique la règle parent, toutes les règles parent sont ignorées. S'il y a d'autres bits dans le parent, ils sont appliqués.

Si vous alliez dans l'autre sens, vous formateriez selon le parent, puis vous écraseriez chaque fois que l'enfant a quelque chose de différent. À long terme, cela représente beaucoup plus de travail que d'ignorer les éléments des règles qui sont déjà pris en charge.

Gregory A Beamer
la source
11
C'est une question distincte. Vous effectuez la cascade en triant les règles par spécificité, puis en les comparant par ordre de spécificité. Mais la question ici est de savoir pourquoi pour une règle donnée, vous faites correspondre ses sélecteurs d'une manière particulière.
Boris Zbarsky