HTMX est une puissante bibliothèque JavaScript qui vous permet de créer des applications web dynamiques avec un minimum de JavaScript. Elle vous permet de faire des requêtes AJAX, d'échanger du contenu HTML et de gérer les événements directement dans vos attributs HTML. J'utilise HTMX depuis environ deux ans à ce stade et avec chaque projet j'en apprends de plus en plus sur ses capacités; et surtout, c'est des limitations.
Cependant, je ne prétends toujours pas en avoir une connaissance experte. Je voulais juste partager certaines des choses que j'ai apprises en cours de route.
Compagnon Article: Cet article se concentre sur le système d'événements, les extensions et la personnalisation avancée de HTMX. Pour les modèles d'intégration ASP.NET Core avec vues partielles, HTMX.NET et pagination, voir mon article d'accompagnement: HTMX avec ASP.NET Core Partials: La Renaissance à l'aide du serveur.
La phase de préparation de la requête est l'endroit où HTMX configure la requête avant qu'elle ne soit envoyée au serveur. Cela comprend le réglage des en-têtes, l'ajout de paramètres et le traitement de l'entrée de l'utilisateur. Les événements suivants sont déclenchés pendant cette phase:
flowchart LR
A[htmx:configRequest] --> B[htmx:confirm] --> C[htmx:prompt] --> D[htmx:abort]
La phase du cycle de vie de la requête est l'endroit où HTMX envoie la requête au serveur et gère la réponse. Les événements suivants sont déclenchés pendant cette phase:
flowchart LR E[htmx:beforeRequest] --> F[htmx:request] --> G[htmx:afterRequest]
La phase de traitement des réponses est l'endroit où HTMX traite la réponse à partir du serveur et met à jour le DOM. Les événements suivants sont déclenchés pendant cette phase:
flowchart LR
H[htmx:beforeOnLoad] --> I[htmx:onLoad]
I --> J[htmx:beforeSwap] --> K[htmx:swap] --> L[htmx:afterSwap]
L --> M[htmx:afterSettle] --> N[htmx:afterOnLoad]
La phase de gestion de l'historique est l'endroit où HTMX met à jour l'historique du navigateur et l'URL. Les événements suivants sont déclenchés pendant cette phase:
flowchart LR A[htmx:historyRestoreRequest] A --> B[htmx:historyPopped] B --> C[htmx:historyRestorePage]
Comme vous pouvez le voir, HTMX fournit un certain nombre d'événements auxquels vous pouvez vous connecter pour modifier la requête ou la réponse, ou même l'historique; HTMX fait un LOT pour être un système aussi compact. En utilisant chacun d'eux, vous pouvez modifier comment HTMX interagit avec le serveur / le client de manière assez complète.
L'un des aspects les plus puissants de HTMX est la capacité de créer des extensions Dans mon cas, j'ai tendance à m'accrocher à htmx:configRequest pour ajouter des paramètres supplémentaires à la demande. Ceci est utile lorsque vous souhaitez transmettre des données supplémentaires au serveur sans avoir à modifier le code HTML ou JavaScript.
D'autres extensions pourraient s'accrocher htmx:beforeRequest modifier la requête avant qu'elle ne soit envoyée; mais après la plupart des autres extensions qui crochetent configRequest; comme dans beforeRequest des trucs comme HX-Vals et HX-Includes sont déjà attachés à la reuest (soit dans la charge utile \ querystring).
Tu peux même t'accrocher htmx:afterSwap pour effectuer des actions après que le contenu a été échangé. Combiné avec le côté client templating bibliothèques comme Alpine.js ou Lit vous pouvez créer des applications dynamiques puissantes avec un code minimal.
HTMX fournit quelques extensions intégrées comme hx-boost et hx-swap-oob qui vous permettent d'améliorer la fonctionnalité de HTMX sans écrire de code personnalisé. Cependant, il ya des moments où vous devez créer vos propres extensions pour répondre à des exigences spécifiques.
Par exemple, vous pouvez ajouter des en-têtes personnalisés à vos demandes, modifier la charge utile de la demande ou gérer des événements spécifiques d'une manière unique.
Pour réaliser ce HTMX vous offre quelques points d'intégration pratiques:
{
/**
* init(api)
* Called once when the extension is initialized.
* Use it to set up internal state, store references, or access HTMX utility functions via the api parameter.
*/
init: function(api) {
return null;
},
/**
* getSelectors()
* Returns additional CSS selectors that HTMX should monitor.
* Useful if your extension needs to handle custom elements or dynamic behavior.
*/
getSelectors: function() {
return null;
},
/**
* onEvent(name, evt)
* Called on every HTMX event (e.g., htmx:beforeRequest, htmx:afterSwap).
* Return false to cancel the event or stop propagation.
*/
onEvent: function(name, evt) {
return true;
},
/**
* transformResponse(text, xhr, elt)
* Modify the raw response text before it is parsed and swapped into the DOM.
* Use this to sanitize or preprocess HTML.
*/
transformResponse: function(text, xhr, elt) {
return text;
},
/**
* isInlineSwap(swapStyle)
* Return true if your extension will handle this swap style manually.
* This tells HTMX to skip default behavior.
*/
isInlineSwap: function(swapStyle) {
return false;
},
/**
* handleSwap(swapStyle, target, fragment, settleInfo)
* Perform custom DOM manipulation if you implement a custom swap style.
* Return true to prevent HTMX's default swap.
*/
handleSwap: function(swapStyle, target, fragment, settleInfo) {
return false;
},
/**
* encodeParameters(xhr, parameters, elt)
* Modify or serialize request parameters before sending.
* Return null to use default URL/form encoding.
* Return a string to override with a custom payload (e.g., JSON).
*/
encodeParameters: function(xhr, parameters, elt) {
return null;
}
}
HTMX fournit un certain nombre d'extensions pré-construites que vous pouvez lire à propos de ici.
Par exemple, une pratique Extension intégrée json-encode vous permet d'envoyer des données JSON dans le corps de la requête au lieu de données de formulaire encodées par URL. Ceci est utile lorsque vous voulez envoyer des structures ou des tableaux de données complexes au serveur.
Vous pouvez voir que cela s'accroche à 3 événements
init - pour configurer l'extension et stocker une référence à l'API HTMXonEvent - pour régler le Content-Type en-tête vers application/json lorsque la requête est configuréeencodeParameters - pour passer outre l'encodage par défaut du formulaire encodé par URL et sérialiser les paramètres comme JSON. Il retourne également une chaîne pour empêcher HTMX d'utiliser l'encodage par défaut du formulaire encodé par URL.(function() {
let api
htmx.defineExtension('json-enc', {
init: function(apiRef) {
api = apiRef
},
onEvent: function(name, evt) {
if (name === 'htmx:configRequest') {
evt.detail.headers['Content-Type'] = 'application/json'
}
},
encodeParameters: function(xhr, parameters, elt) {
xhr.overrideMimeType('text/json')
const object = {}
parameters.forEach(function(value, key) {
if (Object.hasOwn(object, key)) {
if (!Array.isArray(object[key])) {
object[key] = [object[key]]
}
object[key].push(value)
} else {
object[key] = value
}
})
const vals = api.getExpressionVars(elt)
Object.keys(object).forEach(function(key) {
// FormData encodes values as strings, restore hx-vals/hx-vars with their initial types
object[key] = Object.hasOwn(vals, key) ? vals[key] : object[key]
})
return (JSON.stringify(object))
}
})
})()
Ou même le plus simple, mais encore plus MAINTENANT hx-debug extension qui ajoute une HX-Debug Ceci est utile pour le débogage et l'enregistrement, car il vous permet de voir les données de requête brute et de réponse dans la console dev.
(function() {
htmx.defineExtension('debug', {
onEvent: function(name, evt) {
if (console.debug) {
console.debug(name, evt)
} else if (console) {
console.log('DEBUG:', name, evt)
} else {
throw new Error('NO CONSOLE SUPPORTED')
}
}
})
})()
Il y en a beaucoup d'autres, y compris un très puissant extension de templatation côté client qui vous permet d'utiliser les bibliothèques de templatation côté client pour transformer les données JSON retournées en HTML. Ceci est utile pour créer des interfaces dynamométriques sans avoir à compter sur le rendu côté serveur.
Par exemple, dans un projet récent, j'ai utilisé des swaps HTMX OOB pour mettre à jour un certain nombre de lignes dans une table. Pour ce faire, je voulais savoir quelles lignes étaient actuellement affichées dans la table, donc je n'ai mis à jour que les lignes qui étaient visibles.
export default {
encodeParameters: function (xhr, parameters, elt) {
const ext = elt.getAttribute('hx-ext') || '';
if (!ext.split(',').map(e => e.trim()).includes('dynamic-rowids')) {
return null; // Use default behavior
}
const id = elt.dataset.id;
const approve = elt.dataset.approve === 'true';
const minimal = elt.dataset.minimal === 'true';
const single = elt.dataset.single === 'true';
const target = elt.dataset.target;
const payload = { id, approve, minimal, single };
if (approve && target) {
const table = document.querySelector(target);
if (table) {
const rowIds = Array.from(table.querySelectorAll('tr[id^="row-"]'))
.map(row => row.id.replace('row-', ''));
payload.rowIds = rowIds;
}
}
// Merge payload into the parameters object
Object.assign(parameters, payload);
return null; // Return null to continue with default URL-encoded form encoding
}
}
Pour l'utiliser, nous devons ajouter l'extension à notre configuration HTMX. Donc, dans votre fichier js point d'entrée (en supposant que vous utilisez des modules; yoy devrait être) vous pouvez faire quelque chose comme ceci:
import dynamicRowIds from "./dynamicRowIds"; // Load the file
htmx.defineExtension("dynamic-rowids", dynamicRowIds); // Register the extension
Ensuite, sur quel élément vous voulez l'utiliser sur vous pouvez ajouter le hx-ext attribut avec la valeur dynamic-rowids.
<button
hx-ext="dynamic-rowids"
data-target="#my-table"
data-id="@Model.Id"
data-param1="true"
data-param2="false"
data-param3="@Model.Whatever"
hx-post
hx-controller="Contoller"
hx-action="Action"
>
<i class='bx bx-check text-xl text-white'></i>
</button>
Il s'agit d'une autre simple extension HTMX, cette fois accrochée à htmx:configRequest que nous modifions l'URL avant l'envoi de la requête. Cette extension est pratique si vous utilisez le filtrage basé sur requête, etc. et vous voulez que certaines requêtes préservent les filtres existants alors que d'autres ne le font pas (par exemple, 'nom' et 'startdate' mais pas 'page' ou 'sort').
Ceci est SIMILAR à mais pas exactement la même que l'extension HTMX existante Params poussoirs
Vous pouvez voir qu'on s'accroche onEvent d'écouter pour htmx:configRequest l'événement.
Ensuite, nous:
preserve-params-exclude attribut de l'élément (s'il existe) et le diviser en un tableau de touches à exclure (pour que nous ne les ajoutions pas à la requête)export default {
onEvent: function (name, evt) {
if (name !== 'htmx:configRequest') return;
const el = evt.detail.elt;
const excludeStr = el.getAttribute('preserve-params-exclude') || '';
const exclude = excludeStr.split(',').map(s => s.trim());
const currentParams = new URLSearchParams(window.location.search);
const newParams = new URLSearchParams(evt.detail.path.split('?')[1] || '');
currentParams.forEach((value, key) => {
if (!exclude.includes(key) && !newParams.has(key)) {
newParams.set(key, value);
}
});
evt.detail.path = evt.detail.path.split('?')[0] + '?' + newParams.toString();
return true;
}
};
Ici j'utilise l'essentiel HTMX.Net pour ses assistants d'étiquettes. hx-controller et hx-action sont des helpers de tag qui génèrent les attributs HTMX corrects pour vous. hx-route-<x> C'est vraiment utile car il vous permet d'utiliser le code C# pour générer les valeurs correctes pour les attributs au lieu d'avoir à les coder dur dans votre HTML.
Être une extension c'est vraiment simple à utiliser:
Nous devons d'abord ajouter l'extension à notre configuration HTMX.
import preserveParams from './preserveParams.js';
htmx.defineExtension('preserve-params', preserveParams);
REMARQUE : Vous remarquerez que les extensions HTMX par défaut utilisent la méthode « chargement automatique » pour charger l'extension.
// Autoloading the extension and registering it
(function() {
htmx.defineExtension('debug', {
}
C'est une bonne façon de le faire si vous utilisez HTMX dans un environnement non modulaire. Cependant, si vous utilisez des modules (ce que vous devriez être) il est préférable d'utiliser le import déclaration pour charger l'extension puis l'enregistrer explicitement contre votre htmx instance. Cela vous permet de profiter de l'arborescence et de charger uniquement les extensions dont vous avez besoin.
Ensuite, sur votre élément, vous pouvez ajouter le hx-ext attribut avec la valeur preserve-params et les preserve-params-exclude attribut avec une liste de paramètres séparés par des virgules à exclure de la requête.
<a class="btn-outline-icon"
hx-controller="MyController"
hx-action="MyAction"
hx-route-myparam="@MyParam"
hx-push-url="true"
hx-ext="preserve-params"
preserve-params-exclude="page,sort"
hx-target="#page-content"
hx-swap="innerHTML show:top"
hx-indicator>
<i class="bx bx-search"></i>
</a>
En l'espèce, en raison de event.detail.path avoir le nouveau myparam valeur définie dans il remplacera celui avec notre nouvelle valeur mais préserver tous les autres (sauf page et sort). Nous pouvons donc continuer à passer n'importe quel filtre que nous avons défini dans l'URL au serveur sans avoir à nous soucier qu'ils soient perdus lorsque nous faisons une nouvelle requête.
L'une des choses les plus nettes à propos de HTMX est qu'une grande partie de son interaction avec le serveur se fait par le biais d'en-têtes HTTP. Ces en-têtes fournissent au serveur un contexte riche sur ce qui a déclenché la requête, vous permettant de répondre correctement à partir de vos paramètres ASP.NET Core ou vues Razor.
Encore une fois, un élément clé de cette HTMX.Net. Parmi beaucoup d'articles qu'il fournit sont soignés Request Ceci est utile pour déterminer si une demande a été faite par HTMX ou non, et pour la traiter en conséquence.
Il a également son propre mécanisme pour envoyer des déclencheurs
Response.Htmx(h => {
h.WithTrigger("yes")
.WithTrigger("cool", timing: HtmxTriggerTiming.AfterSettle)
.WithTrigger("neat", new { valueForFrontEnd= 42, status= "Done!" }, timing: HtmxTriggerTiming.AfterSwap);
});
Push Urls etc...etc... Khalid a fait un excellent travail de création d'un ensemble d'extensions pour le rendre facile à travailler avec HTMX dans ASP.NET Core.
C'est un outil clé dans ma boîte à outils lorsque vous travaillez avec HTMX et ASP.NET Core. Regarde !
Voici une ventilation des en-têtes les plus utiles que HTMX envoie avec chaque requête :
Description de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête de l'en-tête. |----------------------------|------------------------------------------------------------------------------------------------------------------| HX-Request Toujours réglé à true pour toute demande initiée par HTMX. Idéal pour détecter les appels HTMX dans le middleware ou les contrôleurs. L'identifiant de l'élément cible dans le DOM où la réponse sera échangée. L'identifiant de l'élément qui a déclenché la requête (par exemple un bouton). HX-Trigger-Name Le nom de l'élément déclencheur (utile pour les formulaires). HX-Prompt Contient l'entrée utilisateur de hx-prompt. HX-Current-URL L'URL du navigateur lorsque la requête a été lancée. Utile pour l'enregistrement et le contexte. HX-History-Restore-Request
Je les utilise assez largement dans mes applications ASP.NET Core. Combinées avec les applications HTMX.Net comme Request.IsHtmx() , Request.IsHtmxBoosted() et Request.IsHtmxNonBoosted() vous pouvez facilement détecter les demandes HTMX et répondre en conséquence.
Par exemple, j'ai une extension vraiment simple à Request qui me permet de détecter si une requête vise mon principal #page-content Div. Si c'est alors je sais que je devrais renvoyer une partie.
REMARQUE: Beaucoup ne réalisent pas que vous pouvez spécifier une « page complète » comme une partie, il ne fait que sauter la mise en page.
if (Request.PageContentTarget())
{
Response.PushUrl(Request);
return PartialView("List", vm);
}
return View("List", vm);
public static class RequestExtensions
{
public static bool PageContentTarget(this HttpRequest request)
{
bool isPageContentTarget = request.Headers.TryGetValue("hx-target", out var pageContentHeader)
&& pageContentHeader == "page-content";
return isPageContentTarget;
}
}
En plus des extensions Request, vous pouvez également créer des extensions de réponse pour renvoyer des événements au client. Ceci est utile pour déclencher des événements côté client.
Par exemple dans mon intégration SweetAlert2 J'autorise la fermeture de la boîte de dialogue à l'aide d'un déclencheur défini à partir du serveur.
document.body.addEventListener('sweetalert:close', closeSweetAlertLoader);
Ceci est déclenché à partir du serveur en tant qu'événement déclencheur HTMX.
public static void CloseSweetAlert(this HttpResponse response)
{
response.Headers.Append("HX-Trigger" , JsonSerializer.Serialize(new
{
sweetalert = "close"
}));
}
Cela déclenchera la sweetalert:close événement du côté client, vous permettant de fermer la boîte de dialogue. Vous pouvez également transmettre les données au client en utilisant le HX-Trigger Ceci est utile pour transmettre les données du serveur au client sans avoir à modifier le code HTML ou JavaScript.
Comme vous le voyez, il est facile d'écouter ces événements en ajoutant simplement un auditeur d'événement au corps. J'utilise JSON principalement car il code toujours correctement.
J'ai déjà écrit sur ma méthode de toast. Ici., mais il vaut la peine de mentionner ici aussi . Pour très simplement permettre au serveur de déclencher une notification toast du côté client . J'ai mis le déclencheur dans cette extension Réponse .
public static void ShowToast(this HttpResponse response, string message, bool success = true)
{
response.Headers.Append("HX-Trigger", JsonSerializer.Serialize(new
{
showToast = new
{
toast = message,
issuccess =success
}
}));
}
Je m'accroche ensuite au côté client de l'événement et j'appelle mon showToast fonction.
import { showToast, showHTMXToast } from './toast';
window.showHTMXToast = showHTMXToast;
document.body.addEventListener("showToast", showHTMXToast);
C'est alors que j'appelle showToast fonction et bien, montre un toast; à nouveau voir plus à ce sujet dans l'article .
export function showHTMXToast(event) {
const xhr = event?.detail?.xhr;
let type = 'success';
let message = xhr?.responseText || 'Done!';
try {
const data = xhr ? JSON.parse(xhr.responseText) : event.detail;
if (data.toast) message = data.toast;
if ('issuccess' in data) {
type = data.issuccess === false ? 'error' : 'success';
} else if (xhr?.status >= 400) {
type = 'error';
} else if (xhr?.status >= 300) {
type = 'warning';
}
} catch {
if (xhr?.status >= 400) type = 'error';
else if (xhr?.status >= 300) type = 'warning';
}
showToast(message, 3000, type);
}
Eh bien c'est tout, un tour tourbillon de HTMX et ASP.NET Core. J'espère que vous l'avez trouvé utile et instructif. Si vous avez des questions ou des commentaires s'il vous plaît n'hésitez pas à commenter ci-dessous.
Cet article fait partie d'une série en deux parties sur HTMX avec ASP.NET Core:
Documentation officielle HTMX :
Bibliothèques et outils :
Ressources communautaires :
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.