Passer au contenu

AJAX

Le cœur de htmx est un ensemble d’attributs qui vous permettent d’émettre des requêtes AJAX directement depuis HTML :

AttributDescription
hx-getÉmet une requête GET à l’URL donnée
hx-postÉmet une requête POST à l’URL donnée
hx-putÉmet une requête PUT à l’URL donnée
hx-patchÉmet une requête PATCH à l’URL donnée
hx-deleteÉmet une requête DELETE à l’URL donnée

Chacun de ces attributs prend une URL pour émettre une requête AJAX. L’élément émettra une requête du type spécifié à l’URL donnée lorsque l’élément sera déclenché :

<button hx-put="/messages">
Envoyer vers Messages
</button>

Cela indique au navigateur :

Lorsqu’un utilisateur clique sur ce bouton, émettez une requête PUT vers l’URL /messages et chargez la réponse dans le bouton.

Déclencher des Requêtes

Par défaut, les requêtes AJAX sont déclenchées par l’événement « naturel » d’un élément :

  • input, textarea et select sont déclenchés sur l’événement change.
  • form est déclenché sur l’événement submit.
  • tout le reste est déclenché par l’événement click.

Si vous souhaitez un comportement différent, vous pouvez utiliser l’attribut hx-trigger pour spécifier quel événement déclenchera la requête.

Voici un div qui envoie une requête POST à /mouse_entered lorsqu’une souris entre dedans :

<div hx-post="/mouse_entered" hx-trigger="mouseenter">
[Ici, Souris, Souris !]
</div>

Modificateurs de Déclencheur

Un déclencheur peut également avoir quelques modificateurs supplémentaires qui modifient son comportement. Par exemple, si vous voulez qu’une requête ne se produise qu’une seule fois, vous pouvez utiliser le modificateur once pour le déclencheur :

<div hx-post="/mouse_entered" hx-trigger="mouseenter once">
[Ici, Souris, Souris !]
</div>

D’autres modificateurs que vous pouvez utiliser pour les déclencheurs sont :

  • changed - n’émet une requête que si la valeur de l’élément a changé.
  • delay:<intervalle de temps> - attend le temps donné (par exemple 1s) avant d’émettre la requête. Si l’événement se déclenche à nouveau, le compte à rebours est réinitialisé.
  • throttle:<intervalle de temps> - attend le temps donné (par exemple 1s) avant d’émettre la requête. Contrairement à delay, si un

nouvel événement survient avant la fin du délai, l’événement sera ignoré, de sorte que la requête se déclenchera à la fin de la période.

  • from:<Sélecteur CSS> - écoute l’événement sur un autre élément. Cela peut être utilisé pour des choses comme des raccourcis clavier. Notez que ce sélecteur CSS n’est pas réévalué si la page change.

Vous pouvez utiliser ces attributs pour implémenter de nombreux schémas UX courants, tels que Recherche Active :

<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup changed delay:500ms"
hx-target="#search-results"
placeholder="Rechercher...">
<div id="search-results"></div>

Cet input émettra une requête 500 millisecondes après un événement keyup si l’input a été modifié et insère les résultats dans le div avec l’id search-results.

Plusieurs déclencheurs peuvent être spécifiés dans l’attribut hx-trigger, séparés par des virgules.

Filtres de Déclencheur

Vous pouvez également appliquer des filtres de déclencheur en utilisant des crochets après le nom de l’événement, englobant une expression JavaScript qui sera évaluée. Si l’expression évalue à true, l’événement se déclenchera, sinon il ne se déclenchera pas.

Voici un exemple qui ne se déclenche que sur un clic avec contrôle de l’élément :

<div hx-get="/clicked" hx-trigger="click[ctrlKey]">
Contrôle Clic Moi
</div>

Les propriétés comme ctrlKey seront résolues par rapport à l’événement déclencheur d’abord, puis par rapport à la portée globale. Le symbole this sera défini sur l’élément actuel.

Événements Spéciaux

htmx fournit quelques événements spéciaux pour être utilisés dans hx-trigger :

  • load - se déclenche une fois lorsque l’élément est d’abord chargé.
  • revealed - se déclenche une fois lorsqu’un élément entre pour la première fois dans le champ de vision.
  • intersect - se déclenche une fois lorsqu’un élément intercepte pour la première fois le champ de vision. Cela prend en charge deux options supplémentaires :
    • root:<sélecteur> - un sélecteur CSS de l’élément racine pour l’intersection.
    • threshold:<flottant> - un nombre flottant entre 0,0 et 1,0, indiquant la quantité d’intersection pour déclencher l’événement.

Vous pouvez également utiliser des événements personnalisés pour déclencher des requêtes si vous avez un cas d’utilisation avancé.

Polling

Si vous voulez qu’un élément sonde l’URL donnée plutôt que d’attendre un événement, vous pouvez utiliser la syntaxe every avec l’attribut hx-trigger :

<div hx-get="/news" hx-trigger="every 2s"></div>

Cela indique à htmx :

Toutes les 2 secondes, émettez une requête GET à /news et chargez la réponse dans le div.

Si vous souhaitez arrêter le sondage depuis une réponse du serveur, vous pouvez répondre avec le code de réponse HTTP 286 et l’élément annulera le sondage.

Polling de Chargement

Une autre technique pouvant être utilisée pour réaliser du polling dans htmx est le « polling de chargement », où un élément spécifie un déclencheur load avec un délai et se remplace par la réponse :

<div hx-get="/messages"
hx-trigger="load delay:1s"
hx-swap="outerHTML">
</div>

Si le point de terminaison /messages continue de renvoyer un div configuré de cette manière, il continuera à « sonder » l’URL toutes les secondes.

Le polling de chargement peut être utile dans des situations où un sondage a un point final où le sondage se termine, comme lorsque vous montrez une barre de progression à l’utilisateur.

Indicateurs de Requête

Lorsqu’une requête AJAX est émise, il est souvent bon de faire savoir à l’utilisateur que quelque chose se passe, car le navigateur ne lui donnera aucun retour visuel. Vous pouvez accomplir cela dans htmx en utilisant la classe htmx-indicator.

La classe htmx-indicator est définie de manière à ce que l’opacité de tout élément ayant cette classe soit de 0 par défaut, le rendant invisible mais présent dans le DOM.

Lorsque htmx émet une requête, il ajoutera une classe htmx-request à un élément (soit l’élément demandeur soit un autre élément, si spécifié). La classe htmx-request fera passer un élément enfant avec la classe htmx-indicator à une opacité de 1, révélant l’indicateur.

<button hx-get="/click">
Cliquez-moi !
<img class="htmx-indicator" src="/spinner.gif">
</button>

Ici, nous avons un bouton. Lorsqu’il est cliqué, la classe htmx-request sera ajoutée, ce qui révélera l’élément gif spinner. (J’aime les spinners SVG ces jours-ci.)

Bien que la classe htmx-indicator utilise l’opacité pour masquer et montrer l’indicateur de progression, si vous préférez un autre mécanisme, vous pouvez créer votre propre transition CSS ainsi :

.htmx-indicator {
display: none;
}
.htmx-request .htmx-indicator {
display: inline;
}
.htmx-request.htmx-indicator {
display: inline;
}

Si vous voulez que la classe htmx-request soit ajoutée à un autre élément, vous pouvez utiliser l’attribut hx-indicator avec un sélecteur CSS pour ce faire :

<div>
<button hx-get="/click" hx-indicator="#indicator">
Cliquez-moi !
</button>
<img id="indicator" class="htmx-indicator" src="/spinner.gif">
</div>

Ici, nous appelons explicitement l’indicateur par son id. Notez que nous aurions pu placer la classe sur le div parent et avoir le même effet.

Vous pouvez également ajouter l’attribut disabled aux éléments pendant la durée d’une requête en utilisant l’attribut hx-disabled-elt.

Cibles

Si vous souhaitez que la réponse soit chargée dans un autre élément que celui qui a fait la requête, vous pouvez utiliser l’attribut hx-target, qui prend un sélecteur CSS. Revenons à notre exemple de recherche en direct :

<input type="text" name="q"
hx-get="/trigger_delay"
hx-trigger="keyup delay:500ms changed"
hx-target="#search-results"
placeholder="Rechercher...">
<div id="search-results"></div>

Vous pouvez voir que les résultats de la recherche seront chargés dans le div#search-results, plutôt que dans l’input.

Sélecteurs CSS Étendus

hx-target, et la plupart des attributs qui acceptent un sélecteur CSS, prennent en charge une syntaxe CSS « étendue » :

  • Vous pouvez utiliser le mot-clé this, qui indique que l’élément sur lequel l’attribut hx-target est placé est la cible.
  • La syntaxe closest <Sélecteur CSS> trouvera l’élément ancêtre le plus proche ou lui-même, qui correspond au sélecteur CSS donné (par exemple, closest tr ciblera la ligne de tableau la plus proche de l’élément).
  • La syntaxe next <Sélecteur CSS> trouvera l’élément suivant dans le DOM correspondant au sélecteur CSS donné.
  • La syntaxe previous <Sélecteur CSS> trouvera l’élément précédent dans le DOM correspondant au sélecteur CSS donné.
  • find <Sélecteur CSS> trouvera le premier enfant descendant correspondant au sélecteur CSS donné (par exemple, find tr ciblera la première ligne de tableau descendante par rapport à l’élément).

De plus, un sélecteur CSS peut être enveloppé dans les caractères < et />, imitant la syntaxe du littéral de requête de hyperscript.

Les cibles relatives comme celle-ci peuvent être utiles pour créer des interfaces utilisateur flexibles sans parsemer votre DOM de nombreux attributs id.

Swapping

htmx offre plusieurs façons différentes de remplacer le HTML renvoyé dans le DOM. Par défaut, le contenu remplace le innerHTML de l’élément cible. Vous pouvez modifier cela en utilisant l’attribut [hx-swap](https://htmx.org/attributes/hx

-swap/) avec l’une des valeurs suivantes :

NomDescription
innerHTMLpar défaut, insère le contenu à l’intérieur de l’élément cible
outerHTMLremplace entièrement l’élément cible par le contenu renvoyé
afterbegininsère le contenu avant le premier enfant à l’intérieur de la cible
beforebegininsère le contenu avant la cible dans l’élément parent de la cible
beforeendinsère le contenu après le dernier enfant à l’intérieur de la cible
afterendinsère le contenu après la cible dans l’élément parent de la cible
deletesupprime l’élément cible, quelle que soit la réponse
nonen’ajoute pas de contenu provenant de la réponse (les Swaps Hors Bande et les En-têtes de Réponse seront toujours traités)

Morphing

En plus des mécanismes de remplacement standard ci-dessus, htmx prend également en charge les swaps de type morphing, via des extensions. Les swaps de type morphing tentent de fusionner le nouveau contenu dans le DOM existant, plutôt que de simplement le remplacer. Ils préservent souvent mieux des éléments comme le focus, l’état vidéo, etc., en modifiant les nœuds existants sur place pendant l’opération de swap, au prix d’une utilisation accrue du processeur.

Les extensions suivantes sont disponibles pour les swaps de type morphing :

Transitions de Vue

La nouvelle API View Transitions expérimentale donne aux développeurs un moyen de créer une transition animée entre différents états du DOM. Elle est encore en développement actif et n’est pas disponible dans tous les navigateurs, mais htmx fournit un moyen de travailler avec cette nouvelle API qui revient au mécanisme sans transition si l’API n’est pas disponible dans un navigateur donné.

Vous pouvez expérimenter avec cette nouvelle API en utilisant les approches suivantes :

  • Définissez la variable de configuration htmx.config.globalViewTransitions sur true pour utiliser des transitions pour tous les swaps.
  • Utilisez l’option transition:true dans l’attribut hx-swap.
  • Si un swap d’élément est sur le point d’être transitionné en raison de l’une des configurations ci-dessus, vous pouvez intercepter l’événement htmx:beforeTransition et appeler preventDefault() dessus pour annuler la transition.

Les Transitions de Vue peuvent être configurées à l’aide de CSS, comme décrit dans la documentation Chrome pour la fonctionnalité.

Vous pouvez voir un exemple de transition de vue sur la page d’exemples d’animations.

Options de Swap

L’attribut hx-swap prend en charge de nombreuses options pour régler le comportement de swapping de htmx. Par exemple, par défaut htmx remplacera le titre d’une balise title trouvée n’importe où dans le nouveau contenu. Vous pouvez désactiver ce comportement en définissant le modificateur ignoreTitle à true :

<button hx-post="/like" hx-swap="outerHTML ignoreTitle:true">J'aime</button>

Les modificateurs disponibles sur hx-swap sont :

OptionDescription
transitiontrue ou false, indique si l’API de transition de vue doit être utilisée pour ce swap
swapLe délai de swap à utiliser (par exemple, 100ms) entre le moment où l’ancien contenu est supprimé et le nouveau contenu est inséré
settleLe délai de stabilisation à utiliser (par exemple, 100ms) entre le moment où le nouveau contenu est inséré et le moment où il est stabilisé
ignoreTitleSi défini sur true, tout titre trouvé dans le nouveau contenu sera ignoré et ne mettra pas à jour le titre du document
scrolltop ou bottom, fait défiler l’élément cible vers son haut ou bas
showtop ou bottom, fait défiler le haut ou le bas de l’élément cible dans la vue

Tous les modificateurs de swap apparaissent après le style de swap spécifié et sont séparés par des deux-points.

Voir la documentation de l’attribut hx-swap pour plus de détails sur ces options.

Synchronisation

Souvent, vous souhaitez coordonner les requêtes entre deux éléments. Par exemple, vous pouvez vouloir qu’une requête d’un élément remplace la requête d’un autre élément ou attende que la requête de l’autre élément soit terminée.

htmx propose un attribut hx-sync pour vous aider à accomplir cela.

Considérez une condition de concurrence entre une soumission de formulaire et une requête de validation d’un input individuel dans ce HTML :

<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change">
<button type="submit">Soumettre</button>
</form>

Sans utiliser hx-sync, remplir l’input et soumettre immédiatement le formulaire déclenche deux requêtes parallèles vers /validate et /store.

Utiliser hx-sync="closest form:abort" sur l’input surveillera les requêtes sur le formulaire et annulera la requête de l’input si une requête de formulaire est présente ou commence pendant que la requête de l’input est en cours :

<form hx-post="/store">
<input id="title" name="title" type="text"
hx-post="/validate"
hx-trigger="change"
hx-sync="closest form:abort">
<button type="submit">Soumettre</button>
</form>

Cela résout la synchronisation entre les deux éléments de manière déclarative.

htmx prend également en charge une méthode programmatique pour annuler les requêtes : vous pouvez envoyer l’événement htmx:abort à un élément pour annuler toute requête en cours :

<button id="request-button" hx-post="/example">
Émettre une Requête
</button>
<button onclick="htmx.trigger('#request-button', 'htmx:abort')">
Annuler la Requête
</button>

Plus d’exemples et de détails peuvent être trouvés sur la page de l’attribut hx-sync.

Transitions CSS

htmx facilite l’utilisation des Transitions CSS sans JavaScript. Considérez ce contenu HTML :

<div id="div1">Contenu Original</div>

Imaginez que ce contenu soit remplacé par htmx via une requête AJAX avec ce nouveau contenu :

<div id="div1" class="red">Nouveau Contenu</div>

Notez deux choses :

  • Le div a le même id dans le contenu original et dans le nouveau contenu.
  • La classe red a été ajoutée au nouveau contenu.

Dans cette situation, nous pouvons écrire une transition CSS de l’ancien état au nouvel état :

.red {
color: red;
transition: all ease-in 1s;
}

Lorsque htmx remplace ce nouveau contenu, il le fera de manière à ce que la transition CSS s’applique au nouveau contenu, vous offrant une transition fluide vers le nouvel état.

Donc, en résumé, tout ce que vous avez à faire pour utiliser les transitions CSS pour un élément est de garder son id stable entre les requêtes !

Vous pouvez voir les Exemples d’Animations pour plus de détails et des démonstrations en direct.

Détails

Pour comprendre comment les transitions CSS fonctionnent réellement dans htmx, vous devez comprendre le modèle sous-jacent de remplacement et de stabilisation que htmx utilise.

Lorsque du nouveau contenu est reçu du serveur, avant que le contenu ne soit remplacé, le contenu existant de la page est examiné pour les éléments qui correspondent par l’attribut id.

Si une correspondance est trouvée pour un élément dans le nouveau contenu, les attributs de l’ancien contenu sont copiés sur le nouvel élément avant que le remplacement n’ait lieu. Le nouveau contenu est ensuite remplacé, mais avec les anciennes valeurs d’attributs. Enfin, les nouvelles valeurs d’attributs sont remplacées, après un délai de stabilisation (20ms par défaut). Un peu fou, mais c’est ce qui permet aux transitions CSS de fonctionner sans JavaScript par le développeur.

Swaps Hors Bande

Si vous souhaitez échanger du contenu d’une réponse directement dans le DOM en utilisant l’attribut id, vous pouvez utiliser l’attribut hx-swap-oob dans le HTML de la réponse :

<div id="message" hx-swap-oob="true">Remplacez-moi directement !</div>
Contenu Supplémentaire

Dans cette réponse, div#message serait échangé directement dans l’élément DOM correspondant, tandis que le contenu supplémentaire serait échangé dans la cible de manière normale.

Vous pouvez utiliser cette technique pour « chevaucher » les mises à jour sur d’autres requêtes.

Tables Problématiques

Les éléments de table peuvent poser problème lorsqu’ils sont combinés avec des swaps hors bande, car, selon la spécification HTML, beaucoup ne peuvent pas exister seuls dans le DOM (par exemple, <tr> ou <td>).

Pour éviter ce problème, vous pouvez utiliser une balise template pour encapsuler ces éléments :

<template>
<tr id="message" hx-swap-oob="true"><td>Joe</td><td>Smith</td></tr>
</template>

Sélection du Contenu à Remplacer

Si vous souhaitez sélectionner un sous-ensemble du HTML de la réponse à échanger dans la cible, vous pouvez utiliser l’attribut hx-select, qui prend un sélecteur CSS et sélectionne les éléments correspondants dans la réponse.

Vous pouvez également choisir des morceaux de contenu pour un swap hors bande en utilisant l’attribut hx-select-oob, qui prend une liste d’identifiants d’éléments à choisir et à échanger.

Préservation du Contenu Pendant un Swap

Si vous souhaitez préserver un contenu à travers les swaps (par exemple, un lecteur vidéo que vous souhaitez laisser jouer même si un swap se produit), vous pouvez utiliser l’attribut hx-preserve sur les éléments que vous souhaitez préserver.

Paramètres

Par défaut, un élément qui provoque une requête inclura sa valeur s’il en a une. Si l’élément est un formulaire, il inclura les valeurs de tous les champs de saisie qu’il contient.

Comme pour les formulaires HTML, l’attribut name du champ de saisie est utilisé comme nom de paramètre dans la requête envoyée par htmx.

De plus, si l’élément provoque une requête autre qu’un GET, les valeurs de tous les champs de saisie du formulaire englobant le plus proche seront incluses.

Si vous souhaitez inclure les valeurs d’autres éléments, vous pouvez utiliser l’attribut hx-include avec un sélecteur CSS de tous les éléments dont vous souhaitez inclure les valeurs dans la requête.

Si vous souhaitez filtrer certains paramètres, vous pouvez utiliser l’attribut hx-params.

Enfin, si vous souhaitez modifier les paramètres de manière programmatique, vous pouvez utiliser l’événement htmx:configRequest .

Téléchargement de Fichiers

Si vous souhaitez télécharger des fichiers via une requête htmx, vous pouvez définir l’attribut hx-encoding sur multipart/form-data. Cela utilisera un objet FormData pour soumettre la requête, qui inclura correctement le fichier dans la requête.

Notez qu’en fonction de votre technologie côté serveur, vous devrez peut-être traiter les requêtes avec ce type de contenu de manière très différente.

Notez que htmx déclenche un événement htmx:xhr:progress périodiquement basé sur l’événement progress standard pendant le téléchargement, auquel vous pouvez vous connecter pour montrer la progression du téléchargement.

Consultez la section des exemples pour des modèles de formulaires plus avancés, y compris les barres de progression et la gestion des erreurs.

Valeurs Supplémentaires

Vous pouvez inclure des valeurs supplémentaires dans une requête en utilisant les attributs hx-vals (paires nom-expression au format JSON) et hx-vars (paires nom-expression séparées par des virgules et calculées dynamiquement).

Confirmation des Requêtes

Souvent, vous voudrez confirmer une action avant d’émettre une requête. htmx prend en charge l’attribut hx-confirm, qui vous permet de confirmer une action à l’aide d’une simple boîte de dialogue JavaScript :

<button hx-delete="/account" hx-confirm="Êtes-vous sûr de vouloir supprimer votre compte ?">
Supprimer mon compte
</button>

En utilisant des événements, vous pouvez implémenter des boîtes de dialogue de confirmation plus sophistiquées. L’exemple de confirmation montre comment utiliser la bibliothèque sweetalert2 pour confirmer les actions htmx.

Confirmation des Requêtes avec des Événements

Une autre option pour faire une confirmation est via l’événement htmx:confirm . Cet événement est déclenché sur chaque déclencheur de requête (pas seulement sur les éléments qui ont un attribut hx-confirm) et peut être utilisé pour implémenter une confirmation asynchrone de la requête.

Voici un exemple utilisant sweetalert sur n’importe quel élément avec un attribut confirm-with-sweet-alert='true' :

document.body.addEventListener('htmx:confirm', function(evt) {
if (evt.target.matches("[confirm-with-sweet-alert='true']")) {
evt.preventDefault();
swal({
title: "Êtes-vous sûr ?",
text: "Êtes-vous vraiment sûr ?",
icon: "warning",
buttons: true,
dangerMode: true,
}).then((confirmed) => {
if (confirmed) {
evt.detail.issueRequest();
}
});
}
});