NOTE: Apart from
(and even then it's questionable, I'm Scottish). These are machine translated in languages I don't read. If they're terrible please contact me.
You can see how this translation was done in this article.
Friday, 02 May 2025
//10 minute read
HTMX è una potente libreria JavaScript che consente di creare applicazioni web dinamiche con un minimo di JavaScript. Consente di effettuare richieste AJAX, scambiare contenuti HTML e gestire gli eventi direttamente negli attributi HTML. Sto usando HTMX per circa due anni a questo punto e con ogni progetto imparo sempre di più sulle sue capacità; e soprattutto sono limitazioni.
Tuttavia ancora non pretendo di avere una conoscenza esperta di esso. Volevo solo condividere alcune delle cose che ho imparato lungo il percorso.
La fase di preparazione della richiesta è quella in cui HTMX imposta la richiesta prima dell'uscita. Ciò include la personalizzazione delle intestazioni, la gestione della conferma dell'utente e la raccolta degli input. I seguenti eventi consentono un controllo a grana fine su questi comportamenti:
htmx:configRequest
Incendio prima dell'invio della richiesta. Consente di modificare le intestazioni, i parametri o l'URL di richiesta.htmx:confirm
Incendio per consentire le finestre di conferma. Annulla l'evento se l'utente declina.htmx:prompt
Licenziato quando un hx-prompt
l'attributo è presente. Puoi scavalcare il comportamento immediato qui.htmx:abort
Incendio prima dell'invio della richiesta. In caso di annullamento, la richiesta viene interrotta del tutto.Una volta configurata la richiesta, HTMX procede con l'invio al server. Questa fase comprende l'invio, il monitoraggio e la gestione delle risposte a livello di rete:
htmx:beforeRequest
Licenziato poco prima che la richiesta sia inviata. Utile per la registrazione o le regolazioni finali.htmx:request
Licenziato immediatamente dopo l'invio della richiesta.htmx:afterRequest
Licenziato quando la richiesta viene completata (con successo o con errore), prima della gestione della risposta.Dopo che il server risponde, HTMX gestisce come la risposta aggiorna il DOM. Questi eventi consentono di agganciarsi a fasi di manipolazione DOM:
htmx:beforeOnLoad
Incendio prima dell'inizio dell'elaborazione della risposta.htmx:onLoad
Incendio una volta che la risposta ha caricato ed è pronto per gli aggiornamenti DOM.htmx:beforeSwap
Licenziato appena prima che il contenuto venga scambiato nel DOM. Puoi annullare o personalizzare lo swap.htmx:swap
Licenziato quando si verifica l'effettivo scambio DOM.htmx:afterSwap
Licenziato dopo che il contenuto è stato scambiato.htmx:afterSettle
Licenziato una volta che animazioni e transizioni hanno completato.htmx:afterOnLoad
Licenziato alla fine del ciclo di vita di risposta dopo che tutto si è risolto.La fase di gestione della cronologia è dove HTMX aggiorna la cronologia del browser e l'URL. Durante questa fase si attivano i seguenti eventi:
htmx:historyRestoreRequest
Incendio quando HTMX rileva una richiesta per ripristinare uno stato di pagina precedente.htmx:historyPopped
Incendio quando l'utente naviga utilizzando i pulsanti back/forward del browser.htmx:historyRestorePage
Incendio quando HTMX ripristina un contenuto della pagina precedente dalla cronologia.HTMX fornisce una ricca serie di eventi del ciclo di vita che consentono di personalizzare profondamente come vengono gestite le richieste e le risposte, rendendolo notevolmente potente per una libreria così leggera.
Uno degli aspetti più potenti di HTMX è la capacità di crea estensioni estendere le sue capacità. Nel mio caso, di solito mi aggrappai htmx:configRequest
aggiungere parametri aggiuntivi alla richiesta. Questo è utile quando si desidera passare dati aggiuntivi al server senza dover modificare il codice HTML o JavaScript.
Altre estensioni potrebbero agganciare htmx:beforeRequest
modificare la richiesta prima dell'invio; ma dopo la maggior parte delle altre estensioni che agganciano configRequest
; come in beforeRequest
roba come... HX-Vals
e HX-Include
s sono già attaccati al réuest (sia nel payload \ querystring).
Puoi anche agganciare htmx:afterSwap
per eseguire azioni dopo che il contenuto è stato scambiato. Combinato con librerie di templating lato client come Alpine.js oppure LitCity name (optional, probably does not need a translation) è possibile creare potenti applicazioni dinamiche con codice minimo.
HTMX fornisce alcune estensioni integrate come hx-boost
e hx-swap-oob
che consentono di migliorare la funzionalità di HTMX senza scrivere alcun codice personalizzato. Tuttavia, ci sono momenti in cui è necessario creare le proprie estensioni per soddisfare requisiti specifici.
Ad esempio, potresti voler aggiungere intestazioni personalizzate alle tue richieste, modificare il payload della richiesta o gestire eventi specifici in modo unico.
Per ottenere questo HTMX offre alcuni comodi punti di integrazione:
{
/**
* 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 fornisce una serie di estensioni precostruite è possibile leggi qui.
Per esempio un pratico estensione incorporata json-encode
consente di inviare dati JSON nell'organismo di richiesta invece di dati URL-encoded form. Questo è utile quando si desidera inviare strutture dati complesse o array al server.
Potete vedere che questo aggancia in 3 eventi
init
- impostare l'estensione e memorizzare un riferimento all'API HTMXonEvent
- di impostare il Content-Type
intestazione a application/json
quando la richiesta è configurataencodeParameters
- per sovrascrivere la codifica predefinita URL-encoded form e serializzare i parametri come JSON. Restituisce anche una stringa per impedire ad HTMX di utilizzare la codifica predefinita URL-encoded form.(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))
}
})
})()
O anche il più semplice ma anche più utile hx-debug
estensione che aggiunge a HX-Debug
Intestazione della richiesta. Questo è utile per scopi di debug e registrazione, in quanto consente di visualizzare i dati di richiesta e risposta grezzi nella 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')
}
}
})
})()
Ce ne sono molti di più; compreso un molto potente estensione templatura lato client che consente di utilizzare le librerie di templating lato client per trasformare i dati JSON restituiti in HTML. Questo è utile per creare UI dinamici senza dover fare affidamento sul rendering lato server.
Ad esempio in un recente progetto ho usato HTMX OOB swaps per aggiornare un certo numero di righe in una tabella. Per fare questo volevo sapere quali righe erano attualmente visualizzate nella tabella, quindi ho aggiornato solo le righe che erano visibili.
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
}
}
Per utilizzarlo dobbiamo aggiungere l'estensione alla nostra configurazione HTMX. Quindi nel tuo file js del punto di ingresso (supponendo che stai usando moduli; yoy dovrebbe essere) puoi fare una cosa del genere:
import dynamicRowIds from "./dynamicRowIds"; // Load the file
htmx.defineExtension("dynamic-rowids", dynamicRowIds); // Register the extension
Poi su qualsiasi elemento si desidera utilizzare su si può aggiungere il hx-ext
attributo con il valore 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>
Questa è un'altra semplice estensione HTMX, questa volta agganciata htmx:configRequest
mentre stiamo modificando l'URL prima dell'invio della richiesta. Questa estensione è utile se si utilizza il filtro basato su querystring etc. e vuoi che alcune richieste conservino i filtri esistenti mentre altre no (ad esempio 'name' e'startdate' ma non 'page' o'sort').
Questo è SIMILE a ma non esattamente lo stesso come l'estensione esistente HTMX push-params
Puoi vedere che ci agganciamo onEvent
per ascoltare per il htmx:configRequest
evento.
Allora noi:
preserve-params-exclude
attributo dall'elemento (se esiste) e dividerlo in un array di tasti da escludere (così non li aggiungiamo alla richiesta)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;
}
};
Qui uso l'essenziale HTMX.Net per i suoi aiutanti di tag. I tipi di... hx-controller
e hx-action
sono aiutanti di tag che generano i corretti attributi HTMX per voi. Cosi' come... hx-route-<x>
per i valori da passare nel percorso. Questo è davvero utile in quanto consente di utilizzare il codice C# per generare i valori corretti per gli attributi invece di doverli codificare in modo rigido nel tuo HTML.
Essendo un'estensione è molto semplice da usare:
Per prima cosa dobbiamo aggiungere l'estensione alla nostra configurazione HTMX.
import preserveParams from './preserveParams.js';
htmx.defineExtension('preserve-params', preserveParams);
NOTA: noterete che le estensioni HTMX predefinite usano il metodo 'autoload' per caricare l'estensione.
// Autoloading the extension and registering it
(function() {
htmx.defineExtension('debug', {
}
Questo è un buon modo per farlo se si utilizza HTMX in un ambiente non-module. Tuttavia, se si utilizzano moduli (che si dovrebbe essere) è meglio utilizzare il import
dichiarazione per caricare l'estensione quindi registrarlo esplicitamente contro il vostro htmx
esempio. Questo ti permette di approfittare di tree-shaking e caricare solo le esten