V tomto blogu chcem zhrnúť moderné* možnosti použitia HTTP a HTML v prístupoch tvorby interaktívnych webových aplikácii s veľmi malým množstvom javascriptu.
* moderne tu znamená, že tieto princípy fungovali už keď som začínal s PHP v roku 2008, ale vďaka HTTP/2, novým CSS možnostiam, HTML tagom a knižniciam sa začali používať.
HTMX je jednosúborová knižnica, ktorá v roku 2024 zobrala frontend frameworky útokom, umožňuje tvorbu interaktívnych webových aplikácii (teda toho, čo je teraz doménou SPA). A to celé pomocou server-renderingu a HTML atribútov, pričom od serveru sa chce len to, aby dokázal vrátiť kúsky HTML. Sám autor HTMX dúfa v to, že HTMX zanikne a atribúty, ktoré HTMX pridáva sa dostanú do štandardného HTML.
HTMX je na prvý pohľad knižnica, ktorá pridáva do HTML niekoľko atribútov, ktoré umožňujú spraviť HTTP request z akéhokoľvek tagu na akúkoľvek udalosť a výsledok umiestniť niekam na stránku. To dokáže aj jQuery, tak kde je rozdiel? Rozdiel je v tom, že HTMX je deklaratívne ale hlavne so sebou prináša inú paradigmu v tvorbe UI, je to nástroj, ktorý ponúkla dosť, aby sa v ňom dal spraviť e-shop, biznis aplikáciu, manažment portál,… proste 80% vecí na ktoré sa teraz používa React/Angular/Vue.
Povedzme, že máme SPA aplikáciu, kde klikneme na linku pre načítanie dát do tabuľky podľa zvolených filtrov:
Treba podotknúť, že HTMX dokáže nahradzovať viaceré časti stránky na jeden response a tiež, že v načítaných fragmentoch HTML fungujú jeho atribúty automaticky.
HTMX má vyriešené napríklad lazy-loading, debouncing, loading indocator, vie použiť history API v prehliadača, v príkladoch má ukážky na inline-editing, tranzitions, morphing a mnohé ďalšie.
Na to prečo a ako použiť HTMX sú tu iné články (linkujem ich nižšie), skôr chcem pridať svoje postrehy z troch hoby projektov, ktoré som pomocou HTMX robil:
next table
.hx-swap-oob
, alebo HX-Redirect HTTP hlavičku.Samozrejme HTMX nie je strieborná guľka, ale vďaka nemu viem tvoriť rovnaké webové aplikácie bez NPM, oveľa rýchlejšie, jednoduchšie a z menším počtom riadkov kódov.
Nasleduje príklad, v ktorom sa zobrazujú logy, je v nich možné vyhľadávať, zoraďovať a zobraziť podľa kategórie.
Pri použití HTMX stačí z modrej oblasti spraviť formulár (označený modrou farbou) zo štandardnými HTML elementami a pridať mu tieto atribúty:
<form hx-get="/logs" hx-target="next tbody" hx-trigger="input changed delay:500ms, keyup[key=='Enter'], select changed, submit, load" hx-indicator="#spinner"> …
hx-get
hovorí kam sa má poslať GET požiadavka, na vyhľadanie logov s parametrami (tie sa získajú s inputov),
hx-target
hovorí, že obsah vrátený zo serveru sa umiestni do nasledujúceho tagu tbody
,
hx-trigger
hovorí, na aká udalosti reagovať, je tam zahrnutý debouncing, aj úvodné načítanie,
hx-indicator
hovorí, kde sa nachdza loading inidicator.
Šablóna pre zobrazenie logov (na obrázku oblasti označené zelenou farbou) vyzerá takto:
@foreach (LogEntity log in Model.Logs) { <tr> <td>@log.Time.ToString("dd.MM.yyyy HH:mm:ss")</td> <td>@log.Ip</td> <td>@log.Host</td> <td><span class="@CreateBage(log.Type)">@log.Type</span></td> <td>@log.Content</td> </tr> } <div id="LoadOther" hx-swap-oob="true"> @if (Model.RenderNext) { <button type="button" class="btn btn-outline-secondary" hx-get="@Model.NextUrl" hx-target="previous tbody" hx-trigger="click" hx-indicator="#spinner" hx-swap="beforeend"> Load more </button> } </div>
Server vráti obsah pre tbody
a obsah pre tlačidlo "Load more", ten je oanotovaný atribútom hx-swap-oob="true"
, pretože sa umiestňuje mimo tabuľky a má na sebe rovnaké štyri HTMX tribúty (ale s URL vrátane parametrov) a hx-swap="beforeend"
, aby sa donačítané logy umiestnili na koniec tabuľky.
A to je všetko.
Tu sú nejaké zdroje o HTMX a kedy ho použiť a nepoužiť:
Server-Sent Events je HTTP mechanizmus, ktorým môže server informovať klienta o udalostiach na serveri formou zaslanej správy. Ide o bežný HTTP request, no odpoveď má nastavené TransferEncoding
na chunked
. Nerozdivel od Web-Socketov ide o jednosmerný kanál, ktorý dokáže naplno využiť možnosti protokolu HTTP/2, ale netreba naň ďalší port ani špeciálne pravidlá na firewally.
Pomocou Minimal API v .NET 10 idú jednoducho implementovať:
async IAsyncEnumerable<SseItem<string>> GetStockValues(CancellationToken cancellationToken) { double lastValue = 0.0; while(!cancellationToken.IsCancellationRequested) { double value = Random.Shared.NextDouble() * 100.0; string mark = value > lastValue ? "⇑" : "⇓"; string html = $"<p>SSE coin - {value:.00} EUR <span>{mark}</span></p>"; yield return new SseItem<string>(html); lastValue = value; await Task.Delay(2000 + Random.Shared.Next(-100, 300), cancellationToken); } } app.MapGet("/stockPrice", (CancellationToken cancellationToken) { return TypedResults.ServerSentEvents(GetStockValues(cancellationToken)); });
A jednoducho konzumovať pomocou javascriptu:
const evtSource = new EventSource("/stockPrice"); evtSource.onmessage = (event) => { document.getElementById('stockPrice').innerHTML = JSON.parse(event.data); };
Vďaka HTMX ale nemusíme posielať cez SSE len JSON, ale aj kusy HTML, ktoré je vďaka atribútu hx-swap-oob
umiestniť kamkoľvek na stránku. Takže jedným SSE pripojením je možné riešiť viac udalostí - napríklad aktualizovať ceny SSE coinu a súčasne zobrazovať notifikácie.
Pre HTMX je potrebné použiť oficíalne rozšírenie pre SSE.
<div hx-ext="sse" sse-connect="/stockPrice" sse-swap="message"></div>
Streaming HTML je ako názov napovedá posielanie posielanie kúskov HTML otvoreným spojením. Podobne ako SSE ťaží z HTTP/2.
Predstavte si príklad sociálnej siete, kde pri prvom načítaní stránky, sa vám zobrazí layout s nejakými základnými údajmi, ale zoznam kontaktov, nástenka a notifikácie sa vám načítajú neskôr asynchrónne. Vďaka streamingu HTML a modernému HTML to ide spraviť v jednom requeste, bez toho aby používateľ čakal. Proste sa najskôr pošle celý layout a následne sa server pozrie do svojho dátového úložiska, čo nejaký čas trvá a následne ich pošle ako ďalšie kusy HTML, ktoré prehliadač umiestni na správne miesto.
Ide na to použiť tagy template
a slot
. Ako v nasledujúcej ukážke (pozor na uzatváracie tagy):
<html lang="en"> <head> <meta charset="utf-8" /> </head> <body> <template shadowrootmode="open"> <header> <h1>Streaming HTML - best food</h1> </header> <main> <slot name="content">Loading content…</slot> </main> <footer>Footer (c) 2025</a></footer> </template>
Prvým načítaním sa načítal jednoduchý layout, kde sa zobrazí hlavička a pätička stránky. Na serveri sa zistí, koľko obľúbených jedál sa bude zobrazovať a pošle sa ďalšia časť HTML.
<div slot="content"> <template shadowrootmode="open"> <p>Food list with poularity:</p> <ul> <li><slot name="slot-1">Loading…</slot></li> <li><slot name="slot-2">Loading…</slot></li> <li><slot name="slot-3">Loading…</slot></li> </ul> </template>
A následne sa postupne donačítajú jednotlivé jedlá (tu si môžeme predstaviť, že sa načítajú z nejakého extra pomalého API po jednom).
<span slot="slot-1">Pizza (<em>95%</em>)</span>
<span slot="slot-3">Burger (<em>82%</em>)</span>
<span slot="slot-2">Sushi (<em>84%</em>)</span> </div> </body> </html>
Tento spôsob žiaľ funguje iba pri prvotnom načítaní stránky, takže nejde využiť pri ajaxových volaniach.
Streaming HTML ide pomocou HTMX využiť napríklad na postupné donačítanie tabuľky už na vyrendrovanej stránke, alebo na aktualizáciu rôznych miest cez hx-swap-oob
.
Ukážka použitia v HTMX pomocou rozšírenia chunked-transfer:
<button type="button" hx-ext="chunked-transfer" hx-get="/streaming" hx-target="next div" hx-trigger="click"> Load content </button> <div></div>
Prípadne daného loading efektu dosiahnuť pomocou CSS, kde sa na klienta posiela vždy aktuálne HTML a CSS-kom sa skryjú pochádzajúce verzie (display:none;
na elementy v sekcii a display:block;
na posledný element).
HTMX je zaujímavý nástroj, ktorý umožňuje vytvárať serverom riadené klientske aplikácie. Sám by som ho prirovnal ku nožíku švšvajčiarskej armády pre HTML.
Medzi výčitky voči nemu je, že komplexnosť sa presunula na server, čo je pravda len čiastočne, lebo pri mnohých typoch aplikácii prináša odstránenie niekoľkých vrstiev (napríklad taký router už prehliadač implementuje sám), navyše na serveri sa zvyčajne používa o dosť lepší a výkonnejší jazyk ako v prehliadači.
Za mňa HTMX, SSE a streaming HTML predstavujú odpoveď na komplexnosť forntendu pre bakcned programátorov.