Quelques techniques HTML et CSS pour réduire l’utilisation de JavaScript
De plus en plus de sites web se reposent sur JavaScript pour la majorité des interactions mises à disposition. Cela permet de créer des expériences fort attrayantes mais cela vient aussi quelquefois avec des effets indésirables :
- temps de chargement allongés ;
- sites inutilisables si le code JavaScript provoque des erreurs ou n’arrive pas à se télécharger ;
- ergonomie, réactivité et accessibilité laissant à désirer sans une équipe ayant les moyens d’y faire attention.
Au vu de ces inconvénients, se reposer sur les solutions proposées nativement par les navigateurs permet de bénéficier à peu de frais de l’expertise de la communauté créant les standards du web. Ces solutions ont généralement l’avantage d’utiliser moins de code, réduisant ainsi les efforts de maintenance pour une équipe de développement (par exemple, pas besoin de mettre à jour les librairies utilisées).
Dans cet article, nous allons explorer certaines de ces solutions natives qui sont disponibles pour la majorité de vos utilisateurs et utilisatrices. Nous verrons des exemples mais nous n’entrerons pas dans toutes les subtilités, d’autres ressources le font très bien. Le but est plutôt de vous informer de l’existence de ces techniques.
Rendu bloqué par JavaScript
Avant de parcourir chaque technique, un petit rappel sur un gros inconvénient de l’usage de JavaScript : un navigateur n’a qu’un seul fil d’exécution pour contrôler le rendu de votre page. Lorsque du JavaScript s’exécute, le navigateur met en attente les événements liés aux interactions de l’utilisateur et les modifications de l’interface. Cela peut-être très irritant à l’usage car on a l’impression que la page ne répond pas à nos actions ou que les animations sont saccadées. Un article de Philip Walton détaille ce problème et inclut une démonstration.
Les équipes de développement ont tendance à travailler au quotidien avec des appareils puissants. Cela atténue souvent les méfaits de notre utilisation de JavaScript. N’hésitez donc pas à tester régulièrement sur des appareils plus limités.
(Il est bien possible d’exécuter du JavaScript dans un autre fil d’exécution grâce aux Web Workers mais c’est très rare d’y avoir recours pour du code d’interface.)
Contrôler le nombre de lignes affichées pour un texte
Version JavaScript
Il y a deux manières d’obtenir cela en JavaScript :
- Limiter le nombre de caractères affichés. Cela est très fragile car, hormis pour les polices à chasse fixe, la largeur des caractères est variable. On peut donc se retrouver avec plus de lignes que souhaitées ou un texte tronqué trop tôt.
- Tronquer le contenu de l’élément à tâtons jusqu’à ce que l’élément occupe le nombre de lignes désirées. Cela est fort coûteux car à chaque tentative, il faut demander au navigateur de faire un rendu pour constater si l’on obtient la taille désirée. Et cette technique ne peut-être précise que si elle est utilisée après le rendu avec les polices souhaitées, ce qui peut donner de gros effets de déplacement de contenu.
Dans une page contenant beaucoup de textes à tronquer, cela ralentit fortement l’affichage. De plus, ces deux solutions tronquent totalement le texte, ce qui peut avoir des conséquences sur les moteurs de recherche ou la restitution par les technologies d’assistance.
La taille de la police ou la largeur de l’élément peuvent aussi changer au cours de la vie de la page (volontairement par l’utilisateur ou l’utilisatrice, involontairement par un changement d’orientation de l’appareil). Prendre en compte tous ces cas est bien ennuyeux.
Version native
-webkit-line-clamp
est une propriété CSS permettant d’obtenir le résultat nativement. Disponible depuis dix ans dans Safari, cette propriété a été tellement utilisée que pour des raisons de compatibilité, les autres navigateurs l’ont aussi adoptée et elle est devenue standard. (Oui, avec le préfixe.) Il faut tout de même lui adjoindre d’autres propriétés préfixées pour obtenir le comportement souhaité. Cela est un peu irritant de devoir utiliser des propriétés préfixées mais ici, le préfixe ayant été explicité dans les standards, on ne prend pas de risque.
Hormis Internet Explorer et Firefox avant la version 68, tous les navigateurs supportent cette propriété. L’exemple suivant illustre son utilisation ainsi qu’une solution de repli.
Voir la démonstration des titres tronqués sur
CodePen
Cette solution ne présente aucun souci de performance ou de déplacement de contenu irritants, ni d’impact sur les moteurs de recherche ou les technologies d’assistance. Par contre, elle ne fonctionne pas pour les éléments ayant plusieurs enfants.
Conserver un élément à l’écran quand il est important
On peut vouloir conserver toujours visible des en-têtes, un panier ou une barre d’outils. Je rencontre souvent ce genre de comportement mais plus rarement sa correcte implémentation.
Version JavaScript
Historiquement, pour faire cela, il fallait écouter les événements de défilement très souvent envoyés. Tellement souvent que la plupart des solutions ignorent la plupart des événements avec des solutions de throttling ou debounce qui filtrent alors leur nombre. De nos jours, on peut utiliser IntersectionObserver
pour ne recevoir des événements que lorsque l’élément entre ou sort de l’écran. C’est déjà bien plus économe.
Une fois que l’on a détecté l’entrée ou la sortie de l’élément dans la fenêtre, il faut passer d’une position: relative
à une position: fixed
. Cela demande au navigateur de recalculer les tailles et positions des éléments (qu’on appelle « faire un layout » ou « reflow ») ce qui est coûteux. Et justement, il faut s’assurer que les éléments autour ne se déplacent pas pour ne pas provoquer un saut du contenu. « Le Monde » souffre par exemple de cette implémentation imparfaite.
Si jamais le rendu est bloqué lorsque l’élément entre ou sort de la fenêtre (ce qui est fort probable avec la tendance actuelle consistant à avoir des animations coordonnées avec le défilement), la bascule ne s’effectuera que bien plus tard.
Version native
CSS dispose maintenant d’une position: sticky
pour obtenir ce comportement. Aucun problème de performance, de réactivité ou de saut de contenu : tant que le navigateur peut défiler, il gardera l’élément positionné exactement où vous l’avez déclaré. Pour choisir son positionnement, on utilise top
, bottom
, left
ou right
.
Voir la démonstration de la liste de noms
sticky sur CodePen</a >
Tous les navigateurs sauf Internet Explorer et de vieilles versions de Chrome ou Firefox supportent la position: sticky
. Pour ces anciens navigateurs, les éléments sont en position: static
, ce qui est la valeur par défaut et ne prendront pas en compte les valeurs de top
, bottom
, left
et right
. À garder en tête si vous avez besoin de supporter ces navigateurs. D’anciennes versions de Safari nécessitent d’utiliser le préfixe -webkit-sticky
.
Il existe cependant une limitation : il est impossible de modifier l’apparence d’un élément selon qu’il adhère à l’écran ou non par exemple avec une pseudo-class :stuck
. C’est une limitation générale de CSS. Dans ces cas là, je recommande de combiner les bénéfices de la position: sticky
pour changer l’adhérence d’un élément avec IntersectionObserver
pour en modifier l’apparence (tout en veillant à ne pas en changer les dimensions, afin de ne pas provoquer de sauts de contenu).
Animer le défilement
Version JavaScript
Pour implémenter cela en JavaScript, il faut très régulièrement exécuter du JavaScript qui va modifier la position de défilement. Pour que l’animation soit fluide, aucun JavaScript ne doit bloquer le rendu pendant toute la durée de l’animation.
Il vous faudra aussi choisir une fonction de temporisation. Pour sembler naturelle, il faudra sans doute qu’elle soit différente pour s’adapter aux conventions du système d’exploitation utilisé.
Version native
Grâce à scroll-behavior: smooth
en CSS et {behavior: 'smooth'}
comme option à scroll
, scrollTo
et scrollIntoView
en JavaScript, vous déléguez toutes les questions de fonctions d’animation. Cela permet d’avoir un comportement plus homogène avec l’appareil utilisé.
Safari ne supporte pas encore cela (sauf en activant une préférence cachée) mais la plupart du temps, ce n’est pas bien grave : c’est un exemple classique d’amélioration progressive (progressive enhancement).
Voir la démonstration du défilement animé sur
CodePen</a >
Que ce soit avec la version JavaScript ou la version native, il y a deux points d’accessibilité à respecter : prendre en compte la préférence pour minimiser les animations et mouvements et s’assurer que le focus soit bien transmis.
Défilement avec points d’accroche
Cela permet de créer des diaporamas, des listes horizontales avec des points d’accroche ou des sections prenant la taille de l’écran.
Version JavaScript
Pour réaliser un diaporama, on écoute généralement :
- les événements de contact (
mousedown
,mouseup
,touchstart
,touchend
,pointerdown
oupointerup
) ; - les événements de déplacement du pointeur (
mousemove
,touchmove
oupointermove
).
Bien gérer cela pour tous les types de pointeurs (souris ou doigt) et lorsque le pointeur sort de la zone est très délicat. Une fois que l’on a bien géré ces événements, on déplace les éléments en fonction du mouvement. Chaque déplacement déclenche un rendu, coûteux, ce qui peut créer des saccades brisant l’illusion pour l’internaute.
Pour les sections prenant la taille de l’écran ou les listes horizontales, il faut écouter les événements de défilement, les annuler pour les remplacer par le défilement que l’on souhaite. Obtenir un comportement agréable est très difficile : je pense que tout le monde a déjà enragé devant son écran en rencontrant un site qui détourne le défilement natif. C’est tellement fréquent qu’on parle alors de scroll jacking (vol du défilement).
Pour ces deux usages, il vous faudra décider quand passer à l’élément suivant selon la distance et la vitesse du déplacement initial. Si vos choix ne correspondent pas au comportement du système utilisé, vous frustrez vos utilisateurs ou utilisatrices.
Version native
CSS dispose de scroll snap pour gérer cela. Sur le conteneur du défilement, on définit scroll-snap-type
pour indiquer la direction dans laquelle on souhaite des accroches et si l’accroche est obligatoire ou seulement lorsqu’on est à proximité d’un point d’accroche. Puis sur les enfants de ce conteneur, on définira scroll-snap-align
pour indiquer les points d’accroche.
La démo suivante fonctionne entièrement sans JavaScript. Elle utilise aussi scroll-behavior
pour suggérer à l’utilisateur ou l’utilisatrice la possibilité d’utiliser le défilement habituel.
En cochant la case, on active un tout petit peu de IntersectionObserver
pour mettre en avant la miniature de l’image actuellement affichée.
Voir la démonstration d’un diaporama avec
scroll-snap sur CodePen</a >
Tous les navigateurs récents supportent ce comportement. Il existait une syntaxe alternative mais je ne recommande pas de l’utiliser. Cela augmente le nombre de cas que vous devrez tester et l’on peut se reposer sur la dégradation progressive. Dans ces navigateurs sans support, cela sera un défilement classique.
Comme cette fonctionnalité utilise le défilement normal, on obtient une fluidité sans égale avec les solutions JavaScript, quel que soit le type de pointeur utilisé.
Charger les images à la demande
Version JavaScript
Pour réaliser cela en JavaScript, on utilise la plupart du temps une syntaxe du type <img data-src="…" data-srcset="…" alt="…">
. Lorsque les images se rapprochent de la zone visible, le code JavaScript change alors les attributs pour déclencher le téléchargement et l’affichage des images en question.
Le principal inconvénient de ce système c’est que les images en question ne sont pas visibles tant que le JavaScript associé n’a pas fait son travail. Et ça arrive plus souvent que vous ne le pensez. Les moteurs de recherche auront aussi plus de difficultés à détecter vos images car elles n’existent pas sans JavaScript ou parce qu’ils ne défilent pas.
Il y a aussi des subtilités pour choisir quand déclencher le téléchargement. À quelle distance de la zone visible est-il souhaitable de déclencher en fonction de la bande passante disponible ? Est-ce que vous prenez en compte la vitesse de défilement ?
Version native
Depuis peu, tous les navigateurs sauf Safari supportent l’attribut loading="lazy"
sur les éléments <img>
. Si actuellement votre site charge toutes les images, vous pouvez ajouter cet attribut. C’est un site plus économe pour la majorité des visites pour peu d’efforts de votre côté.
Tant que Safari ne supportera pas cet attribut, si vous utilisez déjà une solution de chargement à la demande, il vous faudra décider selon votre cas particulier. Est-ce que vous pouvez vous permettre que toutes les images se chargent dans Safari ?
Aujourd’hui les règles de déclenchement des téléchargements sont spécifiques à chaque navigateur et peut-être pas idéales. Mais une chose est sûre, les heuristiques s’amélioreront avec le temps et vous n’aurez pas à changer votre code.
Je n’ai pas préparé de démonstration spécifique pour cette fonctionnalité car elle est « invisible » mais vous pouvez me croire, elle fonctionne !
Exemples en production
Si vous souhaitez voir des exemples en milieu réel, j’ai utilisé toutes ces techniques pour mon précédent projet :
- sur les résultats de recherche (titres tronqués et images à la demande) ;
- sur les fiches produits (images à la demande, défilement animé et avec points d’accroche pour la galerie d’images) ;
- et sur le panier (si vous avez des articles, le bouton de commande adhère). Je vous invite à tester successivement avec une fenêtre étroite et avec une fenêtre large.
Conclusion
J’espère que ce petit panorama vous a donné envie de mettre à jour les sites que vous maintenez. La prochaine fois que vous cherchez une librairie JavaScript pour réaliser un comportement, pensez à ces quelques techniques. Demandez-vous aussi s’il n’y a pas encore d’autres techniques HTML ou CSS que je n’ai pas abordées (<details>
et <summary>
ou <datalist>
peut-être). Les navigateurs s’améliorant constamment, vous serez peut-être positivement surpris. Et vos utilisateurs et utilisatrices apprécieront !
2 commentaires sur cet article
Julien, le 22 décembre 2020 à 10:10
Salut Anthony, et merci pour cet article ! Utiliser les possibilités natives des navigateurs impose de d'abord les connaître.
Petite question : je ne comprends pas la phrase "Elle utilise aussi scroll-behavior pour suggérer à l’utilisateur ou l’utilisatrice la possibilité d’utiliser le défilement habituel." (Dans la partie "snap"). Que voulais-tu dire ?
Anthony Ricaud, le 22 décembre 2020 à 12:59
En réponse à Julien.
Si tu commentes les lignes 8 à 10 dans la démo (toute la règle
.smooth-scroll
), tu verras qu'au clic sur une miniature on passe directement à l'image correspondante, sans défilement animé. Avec l'animation suite au clic sur la miniature, l'internaute aura plus de chance de déduire qu'il est possible de faire défiler horizontalement.Il n’est plus possible de laisser un commentaire sur les articles mais la discussion continue sur les réseaux sociaux :