Performance des sélecteurs CSS

Nous avons peu d’informations officielles de la part des navigateurs sur les performances de leurs moteurs. Le Mozilla Developer Center nous propose tout de même une courte page sur comment écrire des feuilles de style efficaces.

Dans ce billet j’appellerai critère une partie de sélecteur qui contient un identifiant, une classe, un nom de balise ou * (par exemple : #back, *:link, .selected, input[type="text"], ou ul.menu). Les directives de l’article de Mozilla peuvent être regroupées en quatre groupes :

  • n’utilisez pas de critère universel ;
  • dans un critère utilisez un identifiant, une classe ou un nom de balise mais pas une combinaison de deux ou trois ;
  • limitez le nombre de critères dans vos sélecteurs ;
  • profitez de la cascade en appliquant les règles le plus haut possible.

Comment ça marche

L’article de Mozilla donne une bonne idée des optimisations du moteur Gecko : le navigateur construit certainement un index pour les noms de balise, les identifiants et les noms de class. Si on utilise un nom de classe, un nom de balise ou un identifiant comme critère le plus à droite, Gecko peut aller rechercher ça dans son index puis après vérifier le reste du sélecteur, de droite à gauche. L’objectif est de savoir si un sélecteur s’applique, uniquement à partir d’une rapide recherche dans les index, sans avoir à analyser le noeud DOM pour récupérer ses autres informations.

Mozilla nous donne même la priorité : Gecko vérifie en priorité les identifiants, puis s’il n’y en a pas il vérifie les noms de classe, sinon il vérifie le nom de la balise, et enfin, si on ne se raccroche à rien, on doit vérifier le sélecteur pour chaque noeud du document.

N’utilisez pas de critère universel

De là vient la première règle : un critère à droite qui n’utilise ni identifiant, ni nom de classe, ni nom de balise, alors il faut parcourir tout le DOM et vérifier le sélecteur sur chaque noeud. Intuitivement on s’attend à ce que ça soit lent, et visiblement ça se vérifie dans le moteur. Fini donc les :link et [type="text"], ça sera a:link et input[type="text"].

Identifiant, classe ou balise, pas de cumul

La seconde règle est aussi induite par le processus de Gecko. Si je spécifie un identifiant et un nom de balise (par exemple ul#menu), Gecko ira chercher l’identifiant dans son index. Pour vérifier que le sélecteur correspond il devra aller chercher le noeud DOM puis comparer le nom de la balise. Au lieu d’aider le navigateur on va lui demander plus de calcul. La réaction est similaire si je fais un ul.menu ou un #menu.selected. Identifiant, nom de classe ou nom de balise, mais un seul des trois, et dans cet ordre de préférence. À la place d’un ul.menu, il vaudrait mieux faire un .menu-list. Certes on perd en généricité mais on demande moins de travail au moteur.

Limitez le nombre de critères

Plus on multiplie les critères plus le moteur doit faire d’analyse via le DOM en plus de la recherche dans son index. Autant que possible, l’identifiant ou le nom de la classe doit être spécifique et ne pas se reposer sur la hiérarchie. Ainsi, Mozilla recommande un .menu-item plutôt qu’un .menu li pour cibler les items d’un menu sous forme de liste. Si vous devez utiliser la hiérarchie et que vous le pouvez (rapport à la compatibilité MSIE 6), toutefois, préférez le sélecteur enfant (signe « supérieur à ») au sélecteur descendant (simple espace).

Profitez de la cascade

Enfin, et c’est là une optimisation qui ne coûte pas cher, appliquez vos styles au noeud le plus générique possible. Pour exemple la couleur de texte et le style des puces peuvent être appliqués directement sur la liste elle même au lieu d’être appliqués sur chaque item de la liste indépendamment. C’est valable pour une bonne partie des styles, justement à cause de l’aspect « cascade » de nos feuilles de style.

Et le reste ?

Il y a une dernière recommandation concernant les images sous forme de sprites, mais c’est en utilisant une règle propriétaire à Mozilla, et j’ai prévu un billet dédié sur le sujet.

Il y a aussi des choses que j’ai cherché mais pas trouvé, que ce soit sur MSDN, le site développeur de Mozilla ou ceux de Opera et Safari : l’influence que les performances de rendu des positions, flottants, marges négatives, tableaux dynamiques et autres méthodes de stylage. Parfois on ressent réellement que telle ou telle page est lourde à cause du montage CSS, et une connaissance de ce est particulièrement coûteux serait plus qu’intéressant. Si parmi mes fidèles cinq lecteurs, l’un de vous a dans son stock un lien sur le sujet, je le remercie de le partager.

Publié par edaspet

Plus d'informations sur mon profil en ligne

14 réponses sur « Performance des sélecteurs CSS »

  1. Aucune idée, ça doit dépendre desquels. Ceux qui se basent sur des corrections d’erreur comme le _width sous MSIE, je pense que l’impact doit être nul ou presque.

  2. Les hacks profitent souvent de faiblesses du parseur (sauf les * html, ou autres destinés à IE essentiellement). Je suis tout aussi intéressé à connaître les différences entre de l’absolu et du flottant en termes de vitesse (spécialement pour des trucs un peu tordu comme les carousels. Genre un carousel avec chaque élément en absolu/relatif vs en flottant.

    Un point connu, qu’il est difficile de mesurer est la taille d’un fond à répéter, un 1×1 px est une catastrophe par rapport à un 50×50, mais dans quelle ordre de grandeur se trouve l’optimum? Des idées de tests à monter pour mesurer cela?

  3. Plus rapide d’accord, mais de quel ordre ? Quels sont les gains ?

    Je trouve aberrant de voir des dizaines de <li class="menu-item">...</li> à la suite…

    Est ce que cela vaut le coup d’optimiser ses sélecteurs CSS quitte à rendre les feuilles de style moins lisibles et le code html plus lourd ?

  4. C’est une question à laquelle je n’ai pas la réponse. Le gain de performance est probablement réduit par rapport à ce que peut apporter le cache ou les modifications qui ont trait au réseau.
    Bref, je doute qu’il soit pertinent de commencer à réécrire ses feuilles de style, par contre ça ne peut pas faire de mal à connaître quand on rédige une nouvelle CSS.

    Pour l’aspect dev+maintenance Vs performance c’est la question persistante quelle que soit la technique mise en oeuvre. Si tu veux faire des tests j’en discuterai avec plaisir, je ne demande que ça.

    Pour l’instant j’ai justement tendance à penser que les gains sont assez faibles et que je perdrai mon temps à faire des tests. Bref, le temps personnel est cher et il y a d’autres choses dans ma liste qui sont AMHA plus importantes à tester.

  5. J’en ai discuté un peu avec Dave Hyatt (auteur original du document) et il disait que ce document été principalement écrit pour les applis en XUL. C’est à dire de gros documents, avec de grosses CSS complexes.

    Il m’a aussi précisé que ces conseils s’adaptent à Webkit (ce n’est pas étonnant). C’était une discussion informelle, ne pas prendre au pied de la lettre, tout ça.

  6. Le gros soucis avec le sélecteur enfant, c’est qu’il n’est pas compatible IE6.
    D’où sa rareté dans les feuilles de style que j’ai pu croiser pour l’instant (je précise que je ne suis pas du tout spécialiste du CSS). Faut-il faire une CSS par navigateur pour optimiser les performances de rendu ou ne pas trop se casser le c** et accepter de faire perdre, quoi ?, quelques µs en écrivant
    p span { … } plutôt que p > span { …} ?

  7. Tout à fait d’accord avec Sunny, quand cela est possible, il est plus « classe » (ptite blague de geek) de faire un code XHTML le plus clean possible. Cela ne veut pas dire que je considère que les classes ou les ID pourrissent la feuille de style (ne caricaturons pas) mais éviter de mettre des classes dans tout les sens permet d’avoir un document XHTML propre, facile à la compréhension et encore une fois, plus facile à maintenir.
    Il y a des cas concret ou pour justement pallier aux faiblesses de IE, on est dans l’obligation de mettre pour le premier élément d’une liste à puce la classe « first-child » afin de créer une pseudo-pseudo-classe 🙂 Mais s’il est possible d’avoir un document XHTML pur, c’est mieux.

    Et puis, il ne faut pas que l’optimisation de la feuille CSS se fasse au détriment du document XHTML et vice versa.

  8. À la place d’un ul.menu, il vaudrait mieux faire un .menu-list. Certes on perd en généricité mais on demande moins de travail au moteur.

    Dans le billet qui fait suite à celui-ci, tu comprends enfin que ces idées sont démesurées comparées aux gains de performances quasi-nuls qu’elles permettent. Bien sûr, ça reste très intéressant à étudier 🙂

    J’avais fait des recherches dans ce sens il y a un certain temps déjà, concernant le sélecteur universel (*). Un développeur Mozilla m’a confirmé que son impact sur les performances était ridicule, et que le seul cas où l’on pouvait fignoler la dessus, c’est le cas d’un site très complexe, sur un lecteur mobile ; dans ce cas seulement, on peut constater éventuellement des ralentissements.

  9. Je n’aime pas ce « enfin » étant donné que dès le départ il est clairement dit que ça ne vaut pas le coup de réécrire quoi que ce soit.
    Maintenant, c’est à voir. Des sites avec plus de 40ko de CSS ça devient courant. Je suis là sur un site à très fort traffic et malheureusement la CSS principale fait 100ko non compressée. Les pages HTML ne sont pas super légères non plus et contiennent bien plus de noeuds DOM que souhaitable.

    Avec le nombre de règles et de noeuds DOM, si un tiers ajoutent un * en fin de règle, les différences sont tout de même visibles. La phase de rendu c’est entre un quart et la moitié de l’occupation du moteur (stat de MSIE), quand ça occupe autant au total on ne peut pas simplement dire « tout est équivalent »

    Mais l’important et l’objectif du billet c’est effectivement surtout la compréhension des mécanismes et du pourquoi on fait tout ça. Je lutte contre les fausses optimisations et les fausses croyances, par exemple les gens qui ajoutent des critères en pensant que ça précise donc que ça accélère.

  10. Je pense que quand on en est à 100 ko de CSS, on a un problème à résoudre :/

    Néanmoins, si contrainte il y a, alors oui je comprend que tu sois obliger de surveiller les performances CSS.

  11. Oh, des problèmes à résoudre il y en a (toujours) plusieurs. Le site parfait n’existe pas et il y a toujours un historique à supporter. Le notre est en partie cette CSS gigantesque (mais pas uniquement)

Les commentaires sont fermés.