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 :

  1. 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.
  2. 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 IntersectionObserverpour 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 ou pointerup) ;
  • les événements de déplacement du pointeur (mousemove, touchmove ou pointermove).

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

  1. Julien, le mardi 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 ?

  2. Anthony Ricaud, le mardi 22 décembre 2020 à 12:59

    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.