Chez Jérémie

Chez Jérémie, parfois c'est sérieux, parfois non !

La préséance des sélecteurs CSS

- Web - Lien permanent

Comment savoir quelle règle CSS va s'appliquer à un éléments, en particulier quand deux règles entre en conflit ? Pour déterminer cela, les sélecteurs CSS on un poids et une priorité qui va permettre aux navigateurs de calculer la préséance des règles à appliquer. Voyons un peu comment ça ce passe.

Avant de se plonger des les arcanes de la méthode de calculs, revenons à quelques petites considérations pratiques, bien utiles dans la vie quotidienne du développeur web :

  • Plus vous ciblez précisément l'élément à styliser, plus il y a de chance que ce soit ce style qui s'applique à cet élément.
  • Firebug est votre ami : lorsque vous sélectionnez un élément dans la vue HTML de Firebug, celui-ci vous montre les styles CSS qui s'y appliquent par ordre d'importance.

Voila pour la vraie vie, maintenant voyons comment ça marche de manière un peu plus savante.

Pour les anglophone puriste, sachez que tout est décris dans la norme CSS 2.1, à cet endroit là très précisément : http://www.w3.org/TR/CSS2/cascade.html#specificity Je vais essayer de vous en faire une retranscription humainement compréhensible.

Priorité et poids !

En CSS, certains sélecteurs sont considérés comme plus prioritaire que d'autre. Concrètement, peu importe le nombre de sélecteurs, certain passerons avant les autres et détermineront donc la règle qui va s'appliquer. La priorité des sélecteurs CSS est la suivante (du plus important au moins important) :

  1. Une déclaration de style "en ligne" (dans le code HTML avec l'attribut style) est toujours prioritaire sur tout le reste (c'est tout de même un cas un peu particulier, j'y reviendrai plus loin)
  2. Le sélecteur d'identifiant #
  3. Les sélecteurs faisant référence à des attributs d'éléments HTML (sélecteurs de classe . et d'attribut []) et les pseudo classes
  4. Le nom des éléments (p, div, strong, etc.) et les pseudo éléments

Si vous êtes attentif, vous aurez remarqué que je n'ai pas parlé des sélecteurs d'enfant, de frère ou de parent ni du sélecteur générique *, c'est normal, ils ne comptent pour rien. Ils n'ont aucune priorité et ils n'ont aucun poids... bref, il ne permettent pas de déterminer une quelconque priorité d'application des règles CSS.

Ainsi, si pour un même élément vous avez une règle de sélection qui utilise le sélecteur #, et une autre qui utilise 50 sélecteurs de classe, c'est la règle avec le sélecteur # qui s'appliquera d'abord. Par exemple, imaginons le cas suivant :

<p id="content" class="alert">Ceci est un message important que je veux afficher en rouge !</p>

Si vous utilisez les règles ci-après, le texte apparaitra en vert (la règle #content est prioritaire sur la règle .alert) :

#content{
    color : #0C0;
}

.alert{
    color : #900;
}

Par contre, avec les règles de sélection suivantes, le texte du paragraphe apparaitra bien en rouge (la règle .alert est prioritaire sur la règle p) :

p{
    color : #0C0;
}

.alert{
    color : #900;
}

C'est seulement quand vous avez des sélecteurs de même priorité dans votre règle de sélection qu'il va falloir calculer le poids de cette règle de sélection. Pour faire simple, c'est la règles qui à le plus de sélecteur de la plus haute priorité qui s'applique.

Une notation pour comprendre

Pour matérialiser cette double notion de priorité (préséance absolue) et de poids (préséance par le nombre), le W3C propose une notation à 4 nombres : "a, b, c, d" ou a représente le nombre de sélecteurs de la plus haute priorité (la déclaration de style en ligne) et d le nombre de sélecteurs de la plus basse priorité (les noms d'élément et les pseudo éléments).

En utilisant cette notation, on peut donc immédiatement voir quel est le style qui va s'appliquer à un élément. Par exemple, si on reprend notre exemple précédent, voila ce que ça donnera :

  1. 0, 1, 0, 0 : #content {...}
  2. 0, 0, 1, 0 : .alert {...}
  3. 0, 0, 0, 1 : p {...}

Vous me direz que sur un exemple aussi simple, c'est s'embêter pour rien. Je ne vous contredirais pas. Mais ça prend du sens quand on commence à avoir des règles un peu plus complexe comme celles-ci :

  1. 0,1,0,1 : p#content {...}
  2. 0,1,0,0 : #content {...}
  3. 0,0,2,1 : p:not(:last-child()) {...}
  4. 0,0,1,2 : h1 + p.alert {...} Notez que le sélecteur + n'est ni compté ni comptable
  5. 0,0,1,1 : p[id="content"] {...} Notez que la sélection d'ID par ce biais est moins prioritaire qu'avec le sélecteur #
  6. 0,0,1,0 : .alert {...}
  7. 0,0,0,2 : body > p {...} Notez que le sélecteur > n'est ni compté ni comptable
  8. 0,0,0,1 : p {...}

Et la cascade dans tout ça ?

Ben oui, CSS ça veut dire : "Cascading Style Sheets", autrement dit "feuilles de style en cascade". Si je vous ai bien seriné avec la méthode de calcul de la préséance des règles de style, il faut savoir que celle-ci s'inscrit dans le cadre plus général de la "cascade" qui est définie comme suit :

  1. On vérifie que des règles sont applicables au type de média concerné (vérification de la porté de la règle @media)
  2. On définie un premier niveau de préséance en fonction de l'origine de la règle :
    1. Les feuilles de style de l'utilisateur avec la règle !important sont toujours appliqués prioritairement sur tout le reste
    2. Les feuilles de style de l'auteur avec la règle !important viennent en suite.
    3. Les feuilles de style de l'auteur sans la règle !important
    4. Les feuilles de style de l'utilisateur sans la règle !important
    5. Les style définie par défaut par le navigateur quand rien d'autre ne s'est appliqué
  3. On calcul la préséance des règles au sein de chaque origine comme vu ci-dessus
  4. Si enfin, pour une même origine donnée, deux règles ont la même préséance, c'est la dernière déclarée (la plus basse dans le code source) qui s'applique.

Dans la plus part des cas, en temps que webdesigner, vous vous situerez quasiment exclusivement dans le cas d'un calcul de préséance sur une feuille de style auteur et vous vous soucierez finalement assez peu de la cascade, mais c'est toujours bon de s'en souvenir, surtout quand un client viens vous dire "oui-mais-ça-marche-pas-chez-moi".

Un petit exemple pour bien comprendre le rôle de la cascade. Si vous définissez la règle #content (préséance 0,1,0,0) dans votre feuille de style auteur et que l'utilisateur définie dans sa propre feuille de style la règle p (préséance 0,0,0,1), c'est bien votre style qui va s'appliquer. Néanmoins, si l'utilisateur rajoute le mot clé !important dans sa règle p, alors ces celle-ci qui s'appliquera, même si la préséance propre à votre règle est plus élevé. La cascade prend le pas sur la préséance.

Un petit mot sur les styles en ligne

L'utilisation de l'attribut HTML style est très problématique. Au delà des questions de séparation fond/forme et de la maintenabilité du code HTML, cet attribut est compliqué à gérer. En effet, si vous relisez ce que j'ai écrit sur la cascade, ou se situe cet attribut en terme d'origine pour déterminer sa préséance ? Je n'ai rien trouvé ni dans la norme CSS ni dans la norme HTML qui détermine explicitement cette origine. Néanmoins, quelques test confirme l'intuition que l'on pourrait avoir, les styles en ligne appartiennent à la 3ème origine : "les feuilles de style de l'auteur sans la règle !important".

Cela veux dire qu'il est possible de surcharger un style en ligne en faisant usage de la règle !important. Ceci étant, je vous encourage vivement à éviter ce genre de recours "de la dernière chance" car cela rendra votre code vraiment très difficile à maintenir.

Voila, avec toute ses informations, vous ne devriez pas avoir de problèmes à comprendre pourquoi vos styles ne sont pas pris en compte alors que vous êtes pourtant sur d'avoir tout bien fait comme il faut... parfois, juste en comptant un peu, on s'en sort :-)

Un peu de lecture ailleurs