Utiliser la balise Link dans la balise Body

Par convention, on a toujours utilisé la balise <link> dans la balise <head>. Cette balise est utilisée —entre autre— pour permettre aux navigateurs de lire et d'appliquer les instructions CSS depuis une feuille CSS.

Par convention dis-je ? Pas réellement car :

  • Côté W3C, la balise <link> telle qu'elle a été créée n'est valide W3C en HTML4, xHTML ou HTML5 uniquement si elle est appelé dans la balise <head>.
  • Côté technique, une propriété CSS est appliquée sur un élément du DOM quand il est rendu sur la page uniquement si elle a été interprétée avant de rencontrer cet élément. Si une feuille CSS est lu en pied de page donc, c'est-à-dire après le rendu d'un élément, il y aura un phénomène de FOUC très dérangeant si la page est demandée depuis un réseau bas débit.

Exemple : Ainsi le code suivant est valide :

<head>
    ...
    <link rel="stylesheet" type="text/css" href="example/css.css" media="screen" />
    ...
</head>

mais

Exemple : le code suivant est invalide :

<body>
    ...
    <link rel="stylesheet" type="text/css" href="example/css.css" media="screen" />
    ...
</body>

Cependant, puisque l'affichage du contenu de la page est mis en attente tant que les fichiers href des <link> ne sont pas téléchargés et analysés par le navigateur, cela participe à ralentir l'affichage du contenu des pages aux yeux de l'utilisateur ce qui mène parfois les outils comme PageSpeed ou GTmetrix a demander de placer les feuilles CSS très lourdes après l'analyse du DOM.

Mais puisque cela n'est pas valide, comment faire pour placer la balise <link> en pied de balise <body> ?

Pour répondre à la demande des outils de rapidité web, il est parfois nécessaire de lier sa feuille en pied de page pour être sur que le contenu soit déjà lisible avant même qu'il ne soit habillé par la CSS.

Cela est totalement valide grâce à la norme HTML5.1 ou HTML+RDFa 1.1 (signifiant également que cela est valide en HTML4 également).

RDFa introduit des attributs permettant de décrire des éléments HTML à l'instar des Microdatas. Parmi ces attributs, il y a property. Et c'est justement ce qui va nous permettre de rendre valide <link> dans le body.

Valide dans Head ou Body ?

Les trois codes ci-dessous font la même chose ; permettre a une page HTML d'être habillée par le contenu d'une feuille CSS.

Standard

Le code ci-dessous n'est pas valide dans le <body> mais est valide dans le <head>.

<html ...>
    ...
    <link rel="stylesheet" type="text/css" href="example/css.css" media="screen" />
    ...
</html>

Microdatas

Le code ci-dessous est valide dans le <head> ainsi que dans le <body>.

<html ... itemscope itemtype="http://schema.org/Thing">
    ...
    <link itemprop="url" type="text/css" href="example/css.css" media="screen" />
    ...
</html>

RDFa

Le code ci-dessous est valide dans le <head> ainsi que dans le <body>.

<html ... typeof="schema:Thing">
    ...
    <link property="schema:name" type="text/css" href="example/css.css" media="screen" />
    ...
</html>

Note : rel ne doit pas être présent si itemprop est en place alors qu'il n'est pas gênant (juste redondant) si property est en place.

Support des navigateurs

Là ou le bat blesse c'est que tous les navigateurs ne supportent pas l'inclusion de feuille de style si la propriété rel n'est pas présente en dépit des recommandations du W3C. Que peut-on faire ?

itemprop n'autorise pas rel

Ceci Ne marche pas, le style n'est pas appliqué

<html ... itemscope itemtype="http://schema.org/Thing">
    ...
    <link itemprop="link" type="text/css" href="example/css.css" media="screen" />
    ...
</html>

Ceci ne fonctionne pas, s'il n'y a pas de propriété rel la feuille n'est pas chargée sur tous les navigateurs. Il suffit de la rajouté alors ? Pas si simple car rel ne doit pas être présent si itemprop est en place.

property seul ne marche pas

Ceci Ne marche pas

<body ... typeof="schema:Thing">
    ...
    <link property="schema:url" type="text/css" href="example/css.css" media="screen" />
    ...
</body>

Ceci ne fonctionne pas, ici aussi, sans la propriété rel la feuille n'est pas chargée sur tous les navigateurs. Cependant cette fois, l'attribut rel ne serait pas gênant.

La solution valide

Ceci Marche

<html ...>
    ...
    <link property rel="stylesheet" href="example/css.css" media="screen" />
    ...
</html>

Ce cas de figure est valide car typeof n'est pas obligatoire et property n'a pas besoin de valeur pour la validation.

On peut conclure qu'ajouter la propriété property en supplément de la propriété rel est une pratique assurant la parfaite validité W3C pour l'usage du chargement de fichier CSS dans la balise <body> via la balise <link>.

Phénomène de FOUC VS accès rapide au contenu important de la page

Et quid du phénomène de FOUC si je mets les CSS en pied de page ? Sans trop entrer dans la technique, des pistes à étudier seraient :

  • De charger avant dans le <head> les fichiers CSS les plus léger ne contenant que des instructions de disposition, de visibilité, d'habillage des textes etc. dans l'optique de présenter le plus rapidement possible du texte lisible et mis en forme mais surtout qu'il n'y ai aucun déplacement d’élément ou changement de taille d'élément quand les feuilles CSS de pied de <body> (les plus lourdes) seront chargées.

  • D'uniquement habiller les éléments au-dessus de la ligne de flottaison avec des feuilles dans le <head> (toujours de manière légère) et d'apporter la couche lourde en pied de page.

  • D'utiliser l'un des deux mécanismes précédents pour la première visite de l'internaute, puis de placer par la suite l'intégralité dans le <head> (même les feuilles lourdes) pour éviter aux navigateurs de faire son rendu deux fois (il n'y aura plus de problème de temps de chargement lié aux feuilles lourdes puisqu'elles auront été mises en cache lors de la première visite).

Dans le cas d'une app web, on peut également imaginer uniquement cacher tout le contenu et habiller dans le <head> un encart dédié à couvrir la page et a notifier que l'interface est en chargement... et charger les feuilles lourdes en pied de page avec comme premier rôle de masquer le message d'attente et de ré-afficher complètement habiller l'intégralité de l'interface.