Créer un défilement infini avec HTMX
Le chargement de contenu au fur et à mesure que les utilisateurs font défiler la page élimine les clics de pagination et crée des expériences de navigation plus fluides. Mais l’implémentation traditionnelle du défilement infini nécessite de gérer des intersection observers, de suivre l’état et d’écrire une quantité considérable de JavaScript. HTMX offre une approche plus simple : un défilement infini piloté par le serveur utilisant des attributs HTML.
Ce guide couvre le pattern canonique de défilement infini HTMX, explique quand utiliser revealed plutôt que intersect, et montre comment construire une implémentation progressivement améliorée qui fonctionne sans JavaScript.
Points clés à retenir
- Le défilement infini HTMX utilise un pattern d’élément chargeur auto-perpétuel où chaque réponse du serveur inclut le prochain chargeur
- Utilisez
revealedpour le défilement pleine page etintersect oncepour les conteneurs défilables avec overflow - Construisez sur des liens de pagination standard pour une amélioration progressive qui fonctionne sans JavaScript
- Le serveur contrôle tout l’état de pagination via l’élément chargeur dans chaque réponse
Comment fonctionne le défilement infini piloté par le serveur
Le pattern de base place des attributs HTMX sur un élément « chargeur » — typiquement le dernier élément de votre liste. Lorsque cet élément devient visible, HTMX demande la page suivante à votre serveur. Le serveur renvoie du HTML contenant à la fois les nouveaux éléments et un nouvel élément chargeur pointant vers la page suivante.
Voici la structure de base :
<div id="items">
<div class="item">First item</div>
<div class="item">Second item</div>
<!-- Last rendered item doubles as the loader -->
<div class="item"
hx-get="/items?page=2"
hx-trigger="revealed"
hx-swap="afterend">
Last item of page 1
</div>
</div>
La réponse du serveur pour la page 2 inclut les éléments et un nouveau chargeur :
<div class="item">First item of page 2</div>
<div class="item">Second item of page 2</div>
<div class="item"
hx-get="/items?page=3"
hx-trigger="revealed"
hx-swap="afterend">
Last item of page 2
</div>
Ce pattern auto-perpétuel continue jusqu’à ce que le serveur renvoie du contenu sans chargeur, signalant la fin des données disponibles.
HTMX Intersect vs Revealed : choisir le bon déclencheur
HTMX fournit deux déclencheurs basés sur la visibilité, et sélectionner le mauvais provoque des bugs courants.
Utilisez revealed pour le défilement pleine page où le document lui-même défile. Ce déclencheur se déclenche lorsqu’un élément entre dans le viewport du navigateur.
Utilisez intersect once lorsque le contenu se trouve à l’intérieur d’un conteneur défilable avec du CSS comme overflow-y: scroll. Le déclencheur revealed surveille le viewport du document, pas les éléments défilables individuels, donc il ne se déclenchera pas correctement dans les conteneurs avec overflow. Le déclencheur intersect s’appuie sur l’API Intersection Observer moderne, qui est prise en charge par tous les navigateurs majeurs.
<!-- Full-page scroll: use revealed -->
<div hx-trigger="revealed" hx-get="/more">...</div>
<!-- Scrollable container: use intersect once -->
<div class="scroll-container" style="overflow-y: scroll; height: 400px;">
<div hx-trigger="intersect once" hx-get="/more">...</div>
</div>
Le modificateur once empêche les requêtes en double si l’élément entre et sort à plusieurs reprises du seuil d’intersection.
Stratégies de swap : afterend vs beforeend
Votre stratégie de swap détermine où le nouveau contenu apparaît et comment éviter les chargements en double.
afterend insère le contenu immédiatement après l’élément chargeur. Utilisez ceci lorsque le chargeur est le dernier élément rendu — la réponse apparaît en dessous.
beforeend ajoute le contenu à l’intérieur d’un conteneur cible. Combinez ceci avec hx-target pour spécifier où vont les éléments :
<div hx-get="/items?page=2"
hx-trigger="revealed"
hx-target="#items"
hx-swap="beforeend">
Loading indicator...
</div>
Avec beforeend, votre chargeur se trouve typiquement à l’extérieur du conteneur d’éléments et est remplacé ou supprimé après le chargement de la dernière page.
Discover how at OpenReplay.com.
Amélioration progressive avec pagination standard
Le défilement infini avec HTMX devrait s’appuyer sur la pagination existante, pas la remplacer entièrement. Commencez avec des liens de pagination fonctionnels :
<div id="items">
{% for item in items %}
<div class="item">{{ item.name }}</div>
{% endfor %}
</div>
<a href="/items?page={{ next_page }}">Load more</a>
Ensuite, améliorez le lien de pagination avec des attributs HTMX :
<a href="/items?page={{ next_page }}"
hx-get="/items?page={{ next_page }}"
hx-trigger="revealed"
hx-target="#items"
hx-swap="beforeend"
hx-select="#items > *">
Load more
</a>
Les utilisateurs sans JavaScript voient la pagination standard. Les utilisateurs avec HTMX obtiennent le défilement infini. Le endpoint serveur reste identique pour les deux.
Mise à jour des éléments d’interface avec les swaps out-of-band
Parfois, vous devez mettre à jour des éléments en dehors de la zone de contenu principale — comme des compteurs d’éléments ou des états de chargement. Les swaps out-of-band HTMX gèrent cela :
<!-- In server response -->
<div class="item">New item</div>
<span id="item-count" hx-swap-oob="outerHTML">Showing 20 of 100</span>
L’attribut hx-swap-oob="outerHTML" indique à HTMX de trouver et remplacer l’élément avec l’ID correspondant n’importe où sur la page, indépendamment de la cible de swap principale.
Stratégies de pagination côté serveur
Votre serveur contrôle entièrement l’état de pagination. Deux approches courantes :
La pagination par offset utilise des numéros de page ou des valeurs d’offset : /items?page=3 ou /items?offset=20. Simple à implémenter mais peut montrer des doublons si des éléments sont ajoutés pendant la navigation.
La pagination par curseur utilise un pointeur vers le dernier élément vu : /items?after=item_xyz. Plus fiable pour le contenu dynamique mais nécessite des identifiants stables.
Les deux approches fonctionnent avec HTMX — le client passe simplement le paramètre que le serveur fournit dans le prochain élément chargeur.
Conclusion
Le défilement infini HTMX déplace la complexité vers le serveur où la logique de pagination existe déjà. Choisissez revealed pour le défilement de document et intersect once pour les conteneurs avec overflow. Construisez sur une pagination standard pour que votre fonctionnalité se dégrade gracieusement. Laissez le serveur piloter l’état via l’élément chargeur de chaque réponse plutôt que de suivre les pages côté client.
FAQ
Cela se produit généralement lors de l'utilisation d'intersect sans le modificateur once. L'élément peut traverser à plusieurs reprises le seuil d'intersection au fur et à mesure que le contenu se charge et que la mise en page change. Ajoutez once à votre déclencheur comme hx-trigger intersect once pour garantir qu'une seule requête se déclenche par élément chargeur.
Ajoutez un attribut hx-indicator pointant vers votre élément de chargement. Par exemple hx-indicator avec une valeur de hash loading sur votre élément chargeur et un span séparé avec id loading contenant votre spinner. HTMX affiche automatiquement cet élément pendant les requêtes et le masque une fois terminé.
HTMX aide à prévenir les requêtes qui se chevauchent, et chaque élément chargeur est généralement configuré pour se déclencher une fois. La réponse du serveur détermine la page suivante. Si vous avez besoin d'une coordination plus stricte, vous pouvez utiliser hx-sync pour contrôler le comportement des requêtes.
Oui. Lorsque les filtres changent, réinitialisez votre conteneur de contenu et mettez à jour l'URL du chargeur pour inclure les paramètres de filtre. Le serveur gère la logique de filtrage et renvoie la première page appropriée. Chaque chargeur suivant inclut les mêmes paramètres de filtre pour maintenir la cohérence entre les pages.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.