Pour des sélecteurs CSS raccourcis

Par Harry Roberts

De manière très générale, je pense que plus un site prend de l’ampleur, plus il faut raccourcir les sélecteurs. Je veux dire par là que si vous voulez produire des sites web extensibles, faciles à maintenir, flexibles et au comportement prévisible, alors vous devez être attentif à ce que vos sélecteurs CSS soient aussi gérables que possible pour les développeurs, et donc aussi courts que possible.

Des CSS raccourcis vont servir à beaucoup de choses :

Augmenter l’efficacité des sélecteurs

J’ai déjà écrit au sujet de l’efficacité des sélecteurs CSS. Dans ce billet, je compte détailler un certain nombre de points plus complexes, donc pour une meilleure compréhension de l’ensemble, je vous recommande de commencer par la lecture de Writing efficient CSS selectors.
Si on met à part certains types de sélecteurs (*{} étant souvent le plus lent, selon la façon dont il est employé ; les ID sont les plus rapides, suivis par les classes ; les descendants sont relativement lents, suivis par les pseudo-sélecteurs), alors on peut dire de façon générale que les sélecteurs les plus courts sont les plus rapides.

On le voit de façon très évidente en comparant ces deux sélecteurs :

html body .entete .nav{}
.nav{}

On voit ici assez clairement que dans le premier exemple, le navigateur doit chercher quatre choses : la classe .nav, puis la classe .entete, puis l’élément body, et enfin l’élément html (car les navigateurs lisent les sélecteurs de droite à gauche).
Dans le second exemple, le navigateur ne cherche qu’une seule chose : la classe .nav. Le navigateur a donc quatre fois moins de travail à faire pour faire fonctionner ce sélecteur. Donc, dès que vous écrivez un sélecteur, essayez de l’élaguer autant que possible. À la place de ul.nav{} (2 points de vérification), écrivez .nav{} (1 seul point). Au lieu de .nav li a{} (3 points), écrivez .nav a{} (2 points).

Il est vrai que la performance des sélecteurs CSS, dans l’ensemble, n’est plus vraiment un point qui doit nous causer énormément de soucis, mais cela ne veut pas dire non plus qu’il faut faire n’importe quoi. J’imagine que pour la plupart d’entre nous, ce n’est pas un drame de perdre 5€ ; et pourtant personne ne glisse volontairement de billets dans la déchiqueteuse... L’efficacité des sélecteurs est un réel problème, donc autant l’améliorer là où vous pouvez le faire très facilement.

Réduire la dépendance vis-à-vis de l’emplacement

Avec des sélecteurs plus courts, vous allez vraisemblablement réduire le nombre de sélecteurs descendants (par ex. .barre_laterale .promo{}) et enfants (par ex. .barre_laterale > .promo{}). En vous débarrassant de ces types de sélecteurs descendants, vous réduisez la nécessité de localiser un élément au sein d’un autre. Reprenons l’exemple .barre_laterale .promo{}...

Avec un sélecteur comme .barre_laterale .promo{} vous ciblez n’importe quel élément promotionnel installé dans un élément marqué par la classe .barre_laterale. Cela signifie qu’il est obligatoire de toujours utiliser un style donné au sein d’un élément donné ; vous vous retrouvez dépendant de l’emplacement.
En remplaçant .barre_laterale .promo{} par quelque chose comme .promo-secondaire{}, vous pouvez maintenant placer l’élément en question partout où vous voulez. Dans la barre latérale, comme précédemment, mais aussi dans le pied de page, dans l’en-tête, ou après un article.
En réduisant les descendants, vous réduisez du même coup la dépendance et vous pouvez gagner en portabilité...

Augmenter la portabilité

Maintenant que vous n’êtes plus contraints par des sélecteurs dépendants de leur emplacement, vous allez voir que vos composants sont beaucoup plus portables. Il devient beaucoup plus simple de déplacer diverses choses, parce que la CSS n’a plus à savoir où loge tel ou tel machin : elle a juste besoin de savoir qu’il existe ! C’est cool !
Une autre manière d’augmenter la portabilité, c’est d’éviter de qualifier les sélecteurs. Un sélecteur qualifié, c’est par exemple ul.nav{}, ou a.bouton{}, ou encore div.contenu{}.

Les sélecteurs qualifiés sont à éviter parce qu’ils réduisent l’efficacité (en obligeant à davantage de points de vérification), mais surtout parce qu’ils vous rattachent de force à des éléments spécifiques. Impossible désormais d’utiliser notre classe .bouton sur un <input> ou sur un <button>, par exemple. Impossible d’appliquer .nav à une liste <ol> pour en faire un fil d’Ariane.

Les sélecteurs ne devraient pas renvoyer à un élément. Votre CSS devrait se moquer éperdument de l’élément auquel vous voulez appliquer un style.
Une autre façon de rendre les sélecteurs plus portables serait... de ne pas s’occuper du tout des éléments. Regardez ceci, par exemple :

/* Styles de base d’un widget */
.widget{}

/* Style amélioré pour le titre d’un widget */
.widget > h2{}

Vous vous retrouvez là avec un sélecteur qui pourrait poser de sérieux problèmes : imaginez que ce <h2> doive devenir un jour un <h3> ? Ou bien imaginez qu’il faille ajouter un autre <h2> enfant de widget mais qui ne soit pas stylé en titre ? Vous venez de vous fabriquer un sélecteur très rigide et pas du tout portable. Ce serait beaucoup mieux d’avoir :

/* Styles de base d’un widget */
.widget{}

/* Style amélioré pour le titre d’un widget */
.widget-titre{}

Vous pouvez maintenant appliquer .widget-titre à n’importe quel élément, un <h4> par exemple, tout en conservant tous les autres <h4> que vous voulez dans le widget, mais sans nécessairement leur appliquer de style de titre. Cool cool !

Diminuer les risques de casse des sélecteurs

Plus un sélecteur est long, plus il nécessite de conditions pour que le navigateur l’applique. Et logiquement, plus il y a d’éléments à vérifier, plus il y a de chances que quelque chose tourne mal. Un sélecteur (certes très exagéré) comme body > div:nth-of-type(2) > article:first-child > p:first-child{} , exemple que je tire de ma présentation Breaking Good Habits, contient pas moins de dix points de vérification : donc dix exigences que le navigateur va devoir vérifier pour savoir si ce sélecteur s’applique.

Il suffit que l’emplacement du div:nth-of-type(2) soit modifié, ou bien que le p:first-child devienne un bloc de citation (blockquote), ou encore que article:first-child ne soit plus enfant de div:nth-of-type(2), bref : il suffit qu’il arrive n’importe quel événement pour que ce sélecteur se retrouve brisé. Si vous remplacez tout cela par une simple classe .intro{} , au pire il n’y aura que cet élément-là qui se brisera, et c’est un risque proche de zéro (il faudrait carrément effacer la classe du HTMLpour en empêcher le fonctionnement).
Des sélecteurs plus courts, c’est statistiquement le meilleur moyen d’empêcher les choses de mal tourner.

Réduire la spécificité

Attention, voilà le gros morceau ! On arrive au cœur de la question...
Des sélecteurs plus longs ont une spécificité plus élevée. La spécificité, c’est un cauchemar : il faut la maintenir à un niveau aussi bas que possible. Vous savez déjà qu’il ne faut surtout pas employer d’ID dans les CSS, mais bien souvent vous ferez tout autant de dégâts si vous employez une chaîne de sélecteurs (enfin, presque).
Un sélecteur comme .widget > h2{} a une spécificité plus élevée (en dehors des autres problèmes déjà évoqués) qu’un sélecteur du genre .widget-titre{}.
.nav li a{} a une spécificité plus élevée que .nav a{} (sans compter que c’est moins efficace). Réduire la longueur du sélecteur, c’est réduire sa spécificité ; et ça, c’est très important. Une spécificité élevée finit par auto-alimenter des conflits de spécificité qu’on ne peut résoudre qu’en créant par la suite des sélecteurs encore plus spécifiques (ou alors en utilisant !important, que la honte s’abatte sur vous). C’est absolument horrible. La façon la plus simple de réduire la spécificité (une fois que vous aurez complètement purgé vos CSS de toute trace d’ID) est d’avoir des sélecteurs raccourcis.

Autoriser davantage d’erreurs dans le code

Voici un exemple très spécifique mais très parlant de la façon dont des sélecteurs courts peuvent autoriser un certain nombre d’erreurs. Je préviens tout de suite qu’on peut avoir deux opinions opposées à ce sujet : soit on dit que cela rend le code beaucoup plus souple et que les problèmes peuvent être traités d’autant plus facilement, soit au contraire on trouve que c’est une approche qui favorise justement l’apparition de problèmes, parce qu’elle est trop permissive. Quoi qu’il en soit, je m’en vais vous raconter une histoire vraie...

Un jour que je travaillais sur un assez gros projet chez Sky, j’ai voulu appliquer mes propres règles et j’ai codé une barre de navigation (verticale) comme ceci :

.nav{ /*styles de la navigation*/ }

/* Nota bene : pas de style pour .nav li puisqu’il s’agit d’une navigation à empilage vertical */

.nav a{ display:block; /*styles supplémentaires */ }

Or, il y avait une erreur dans le CMS qui est passée inaperçue. Voici le code qui était généré :

<ul class="nav">
    <a href="#"></a>
    <a href="#"></a>
    <a href="#"></a>
    <a href="#"></a>
</ul>

Vous voyez le problème ? Aucune <li> ! C’est vrai que ce n’est pas cool du tout, mais comme j’avais employé .nav a{} au lieu de .nav li a{} il n’y a pas eu de souci. Mon code était bien plus tolérant que si j’avais mis une troisième condition. Bien sûr, cela ne rend pas le balisage impeccable pour autant, et c’est vrai que cela laisse passer un balisage de moins bonne qualité que si les sélecteurs avaient été plus complets ; néanmoins chacun peut voir que la CSS a permis à l’ensemble de fonctionner en dépit d’une erreur de balisage. Certes, comme je l’ai déjà dit, on peut m’opposer le contre-argument qui dit qu’un sélecteur plus complet aurait justement repéré l’erreur du CMS illico puisqu’aucun style n’aurait affecté les <a>. Oui, mais ! Quoi qu’on en dise, notre CSS avait assez de souplesse pour tolérer cela. C’est à vous d’en tirer vos propres conclusions, car moi aussi je me suis senti tiraillé entre les deux options, un peu ennuyé que l’erreur n’ait pas été détectée, mais en tout cas c’est là un exemple très spécifique de la façon dont des sélecteurs plus courts peuvent rendre votre code moins sensible aux erreurs.

Le mot de la fin

J’ai dit qu’il s’agissait là d’une règle que j’applique aux sites de taille importante, mais franchement, vous devriez l’appliquer partout. Tout ce dont nous venons de parler intéresse prioritairement les projets d’importance (où l’absence de cette règle se fait sentir douloureusement), mais je vous assure qu’elle vous sera utile sur des projets de toutes tailles. Vraiment.
En conclusion, il est vraiment possible d’améliorer grandement la qualité de notre code : il faut plus de classes et moins de descendants, des sélecteurs courts et portables, des sélecteurs neutres vis-à-vis des éléments, et une approche générale qui consiste à penser à la maintenance et aux modifications à venir au moment de l’écriture des CSS. Les choses peuvent devenir plus efficaces, plus tolérantes, plus souples et plus réutilisables rien qu’en repensant l’usage de l’un des éléments les plus simples et les plus fondamentaux des CSS : les sélecteurs.

Fiche technique

À propos de l'auteur

Harry est consultant en architecture front-end, designer, développeur, auteur et orateur, et vit au Royaume-Uni. Il écrit sur son blog (CSS Wizardry), tweete, donne des conférences, et partage du code permettant d’écrire et de maintenir des feuilles de styles pour sites web de grandes dimensions.

Articles similaires

Voici la liste des dix articles les plus récents en rapport avec cet article :

CSS

© 2001-2016 Pompage Magazine et les auteurs originaux - Hébergé chez Nursit.com ! - RSS / ATOM - About this site