De kern van HTMX: Triggers en Geavanceerde Besturingstechnieken
In eerdere artikelen hebben we de basisprincipes van het versturen van verzoeken naar de server met HTMX besproken.
Met attributen zoals hx-get, hx-post, hx-put en hx-delete hebben we gezien dat je nu veel Ajax-functionaliteit kunt implementeren zonder de fetch()-methode van JavaScript.
Maar wanneer je HTMX gebruikt, wil je vaak meer controle over wanneer een verzoek wordt verzonden, in plaats van alleen hoe het wordt verzonden.
Hier komt hx-trigger om de hoek kijken.
Als hx-get of hx-post attributen zijn die bepalen "wat" er moet gebeuren, dan bepaalt hx-trigger "wanneer" het moet gebeuren.
Zonder hx-trigger zou HTMX slechts een eenvoudige Ajax-tool lijken die alleen verzoeken verzendt wanneer op een knop wordt geklikt. Maar vanaf het moment dat je hx-trigger in HTMX gaat gebruiken, zal je tevredenheid toenemen.
Dankzij hx-trigger zijn bijvoorbeeld de volgende dingen mogelijk zonder JavaScript-code:
-
Zoekverzoeken alleen verzenden nadat de invoer is gestopt
-
Dubbele klikken binnen een korte tijd voorkomen
-
Automatisch vernieuwen met een vast interval
-
Elementen alleen laden wanneer ze op het scherm verschijnen
-
Verzoeken alleen verzenden onder specifieke voorwaarden
-
Prioriteiten aanpassen om conflicten tussen verschillende verzoeken te voorkomen
In het begin dacht ik ook: "Waarom HTMX gebruiken als een paar regels fetch-code volstaan?" Mijn houding, waarbij ik HTMX slechts als een eenvoudige Ajax-tool zag, veranderde volledig nadat ik de krachtige besturingsmogelijkheden van hx-trigger had ervaren.
In dit artikel zullen we de triggers en geavanceerde besturingstechnieken van HTMX behandelen, die als de ware kern van de bibliotheek kunnen worden beschouwd.

HTMX is meer dan alleen een knoptool
Wanneer je HTMX voor het eerst tegenkomt, begin je meestal met een voorbeeld zoals dit:
<button hx-get="/hello/" hx-target="#result">
불러오기
</button>
<div id="result"></div>
Op basis hiervan lijkt HTMX misschien gewoon een "tool die Ajax-verzoeken verzendt wanneer je op een knop klikt".
Natuurlijk is dat op zich al handig. Maar dat is slechts een deel van HTMX.
Het echte punt is dat je het moment en de voorwaarden voor het verzenden van een verzoek direct in HTML kunt declareren.
Denk bijvoorbeeld aan:
-
Het verzenden van een verzoek naar de server bij elke typslag in een zoekbalk resulteert in een slechte gebruikerservaring. Het zou veel natuurlijker zijn als het verzoek pas 500ms nadat de invoer is gestopt, wordt verzonden.
-
Je wilt misschien dat een knop alleen werkt wanneer deze wordt geklikt terwijl de Ctrl-toets is ingedrukt.
-
Of je wilt gegevens alleen ophalen wanneer een specifiek element zichtbaar wordt na het scrollen.
Als je voor elk van deze momenten JavaScript-code zou moeten schrijven, zou de code snel toenemen. HTMX daarentegen, kan dergelijke controles grotendeels uitdrukken met een combinatie van attributen, zonder één regel JavaScript.
Dit is echt verbazingwekkend. De schok die ik voelde toen ik hx-trigger van HTMX voor het eerst zag, was misschien vergelijkbaar met de schok die een C++-ontwikkelaar ervaart bij het zien van Python-code en denkt: 'Wat? Geen typeaanduidingen??'
Basistriggers: click, change, submit
Allereerst is het belangrijk te weten dat HTMX al is ontworpen om goed samen te werken met de standaardgedragingen van HTML-elementen.
Een knop is bijvoorbeeld standaard gekoppeld aan click,
een formulier aan submit,
en invoerelementen aan change, afhankelijk van de situatie.
<button hx-get="/load/" hx-target="#result">
가져오기
</button>
Deze knop verzendt een verzoek bij een klik, zelfs zonder expliciet hx-trigger="click" te specificeren.
Hetzelfde geldt voor formulieren.
<form hx-post="/submit/" hx-target="#result">
<input type="text" name="title">
<button type="submit">전송</button>
</form>
In dit geval wordt het verzoek verzonden bij het indienen van het formulier.
Dit betekent dat HTMX in zeer basale scenario's al vrij intelligent werkt. Maar het deel waar we ons op moeten concentreren, begint hier.
Het zelf declareren van het gewenste moment en de gewenste voorwaarden, voorbij de standaardwaarden. Dat is precies wat we willen doen.
Veelgebruikte standaardgebeurtenissen versus HTMX-specifieke triggers in hx-trigger
Allereerst is het essentieel om de onderstaande tabel te begrijpen. De kern is dat standaard DOM-gebeurtenissen die de browser van nature biedt, uiteraard bruikbaar zijn, plus er zijn enkele HTMX-specifieke triggers.
| Categorie | Waarde | Betekenis | Veelvoorkomende scenario's | Voorbeeld |
|---|---|---|---|---|
| Standaard gebeurtenis | click |
Verzoek bij klik | Knoppen, links, actie-uitvoering | hx-trigger="click" |
| Standaard gebeurtenis | input |
Verzoek bij elke wijziging van invoerwaarde | Realtime zoeken, auto-aanvullen | hx-trigger="input changed delay:500ms" |
| Standaard gebeurtenis | change |
Verzoek wanneer waarde definitief is gewijzigd | select, checkbox, invoer na blur |
hx-trigger="change" |
| Standaard gebeurtenis | submit |
Verzoek bij formulierinzending | Formulierverzending | hx-trigger="submit" |
| Standaard gebeurtenis | keyup |
Verzoek wanneer toets wordt losgelaten | Zoeken op basis van toetsinvoer, snelle reactie | hx-trigger="keyup delay:500ms" |
| Standaard gebeurtenis | keydown |
Verzoek op het moment dat een toets wordt ingedrukt | Hotkeys, toetsenbordinteractie | hx-trigger="keydown[from:body]" |
| Standaard gebeurtenis | mouseup |
Verzoek wanneer muisknop wordt losgelaten | Reactie na slepen/selecteren | hx-trigger="mouseup" |
| HTMX-specifiek | load |
Verzoek zodra element is geladen | Uitgesteld laden, initiële gegevens vullen | hx-trigger="load" |
| HTMX-specifiek | revealed |
Verzoek wanneer element op scherm verschijnt | Oneindig scrollen, lazy loading | hx-trigger="revealed" |
| HTMX-specifiek | intersect |
Verzoek wanneer element kruist met viewport | Nauwkeuriger lazy loading, scroll-gebaseerd laden | hx-trigger="intersect once" |
| HTMX-specifieke syntaxis | every 5s |
Verzoek met een vast interval | Polling, statusupdate | hx-trigger="every 5s" |
| Aangepaste gebeurtenis | my-custom-event |
Verzoek met een zelfgedefinieerde gebeurtenis | Serverheader, JS-integratie, losse gebeurtenisarchitectuur | hx-trigger="itemSaved from:body" |
| Modifier | delay:500ms |
Verzoek alleen als er gedurende de opgegeven tijd geen extra gebeurtenissen zijn | Debouncing, optimalisatie realtime zoeken | hx-trigger="keyup delay:500ms" |
| Modifier | throttle:1s |
Beperkt herhaalde verzoeken binnen een korte tijd | Voorkomen van dubbelklikken, beperken van overmatige verzoeken | hx-trigger="click throttle:1s" |
| Modifier | once |
Beperkt tot één keer triggeren | Eerste lading, eenmalige gebeurtenissen | hx-trigger="intersect once" |
| Modifier | changed |
Verzoek alleen als waarde daadwerkelijk is gewijzigd | Optimalisatie invoervelden, voorkomen van onnodige verzoeken | hx-trigger="input changed delay:500ms" |
| Modifier | from:body |
Specificeert een ander element als doel voor gebeurtenisdetectie | Globale gebeurtenissen ontvangen, aangepaste gebeurtenisverwerking | hx-trigger="itemSaved from:body" |
| Modifier | [condition] |
Verzoek alleen als aan de voorwaarde is voldaan | Combinatie van modificatietoetsen, voorwaarde voor invoerlengte | hx-trigger="click[ctrlKey]" / hx-trigger="keyup[value.length > 1]" |
| Modifier | consume |
Voorkomt dat de gebeurtenis wordt doorgegeven aan bovenliggende elementen | Voorkomen van conflicten bij geneste HTMX-verzoeken | hx-trigger="click consume" |
| Modifier | queue:first |
Behoudt alleen de eerste gebeurtenis bij het in de wachtrij plaatsen van nieuwe gebeurtenissen | Alleen het eerste verzoek behouden bij opeenvolgende invoer | hx-trigger="input queue:first" |
| Modifier | queue:last |
Behoudt alleen de laatste gebeurtenis bij het in de wachtrij plaatsen van nieuwe gebeurtenissen | Zoekbalk, auto-aanvullen | hx-trigger="input queue:last" |
| Modifier | queue:all |
Behoudt alle 발생한 gebeurtenissen in de wachtrij | Wanneer alle gebeurtenissen sequentieel moeten worden verwerkt | hx-trigger="input queue:all" |
| Modifier | queue:none |
Negeert nieuwe gebeurtenissen als er een verzoek bezig is | Volledige blokkering van dubbele verzoeken | hx-trigger="click queue:none" |
De waarde van hx-trigger bestaat meestal uit een gebeurtenis (event), gevolgd door eventuele filters en modifiers.
Je kunt het lezen als: "wanneer er iets gebeurt (event), onder welke voorwaarde (filter), en op welke manier (modifier) moet het worden afgehandeld?"
De vorm is meestal event[filter] modifier modifier.
<input
hx-get="/search/"
hx-trigger="keyup[value.length > 1] changed delay:500ms">
-
keyup→ Wanneer een toets wordt losgelaten -
[value.length > 1]→ Alleen wanneer de invoerwaarde langer is dan 1 teken -
changed delay:500ms→ Verzoek alleen als de waarde is gewijzigd en er gedurende 0,5 seconde geen verdere invoer is geweest
Enkele nuttige voorbeelden
Hoewel de bovenstaande tabel een goed overzicht geeft, wil ik graag enkele van mijn favoriete trigger-voorbeelden laten zien.
Verzoek alleen verzenden nadat invoer is gestopt: delay
Wanneer je functies zoals auto-aanvullen in zoekbalken of realtime filtering maakt, kan het verzenden van een verzoek bij elke typslag de server belasten en de gebruikerservaring onrustig maken.
In dergelijke gevallen maakt het gebruik van delay de ervaring veel vloeiender.
<input type="text"
name="q"
hx-get="/search/"
hx-trigger="keyup delay:500ms"
hx-target="#search-result"
placeholder="Voer een zoekterm in">
<div id="search-result"></div>
Deze code verzendt niet direct een verzoek bij elke toetsaanslag van de gebruiker. In plaats daarvan wordt het verzoek verzonden nadat de invoer is gestopt en er 500ms zijn verstreken.
Dit is in feite debouncing.
Om dit met JavaScript te implementeren, heb je code nodig die timers instelt, eerdere timers annuleert en opnieuw configureert. Maar met HTMX is dit eenvoudigweg een attribuut.
Deze functionaliteit is vrijwel essentieel voor zoek-, auto-aanvul- en filter-UI's.
Niet te vaak verzenden: throttle
Terwijl delay het gevoel geeft van "even wachten nadat de invoer is gestopt, en dan verzenden", ligt throttle dichter bij "het beperken van te frequente verzoeken binnen een korte tijd".
<button hx-post="/like/"
hx-trigger="click throttle:1s"
hx-target="#like-count">
Vind ik leuk
</button>
In dit geval, zelfs als de gebruiker heel snel meerdere keren op de knop klikt, kun je voorkomen dat er binnen 1 seconde overmatig veel opeenvolgende verzoeken worden verzonden.
Dit is vrij nuttig in de volgende situaties:
-
Voorkomen van dubbelklikken
-
Blokkeren van te snelle herhaalde verzoeken
-
Verminderen van serverbelasting
-
Voorkomen van onbedoelde meervoudige uitvoering van dezelfde actie
Vooral bij knoppen zoals "Vind ik leuk", "Opslaan", "Vernieuwen" en "Synchroniseren" is dit iets om te overwegen.
Automatisch verzoeken met een vast interval: every
Een functie die verrassend aantrekkelijk is bij het gebruik van HTMX is every.
Wanneer je een specifiek gebied met een vast interval van de server wilt vernieuwen, hoef je geen aparte polling-logica in JavaScript te schrijven.
<div hx-get="/server-status/"
hx-trigger="every 5s"
hx-target="this">
Serverstatus wordt geladen...
</div>
Deze code verzendt elke 5 seconden een GET-verzoek naar /server-status/ en werkt zichzelf bij met het antwoord.
Er zijn meer toepassingen dan je zou denken:
-
Serverstatus bewaking
-
Weergave van voortgang van taken
-
Update van dashboardcijfers
-
Update van chatmeldingen
-
Eenvoudige realtime informatie op beheerderschermen
Natuurlijk moet je voorzichtig zijn om het niet te misbruiken met te korte intervallen, omdat dit de server kan belasten. Maar indien correct gebruikt, is het vermogen van HTMX om dergelijke functionaliteit met alleen HTML-attributen te bieden zeer aantrekkelijk.
Alleen activeren onder specifieke voorwaarden: Gebeurtenisfiltering
De gebeurtenisfilterfunctie is echt geweldig. Toen ik deze functie voor het eerst tegenkwam, voelde ik oprecht dankbaarheid jegens de ontwikkelaars en bijdragers van HTMX. Het is uitstekend.
In HTMX kun je een voorwaarde toevoegen na een gebeurtenis om te beperken wanneer een verzoek plaatsvindt.
<button hx-delete="/post/123/"
hx-trigger="click[ctrlKey]"
hx-target="#post-123"
hx-swap="outerHTML">
Verwijderen
</button>
Deze code werkt niet met een simpele klik. Het verwijderverzoek wordt alleen geactiveerd wanneer er wordt geklikt terwijl de Ctrl-toets is ingedrukt.
Dergelijke voorwaardelijke triggers zijn kleine details die de gebruikerservaring aanzienlijk verfijnen.
Bijvoorbeeld:
-
Uitvoeren alleen wanneer een specifieke modificatietoets is ingedrukt
-
Uitvoeren alleen wanneer een checkbox is geselecteerd
-
Zoeken alleen wanneer de invoerwaarde een bepaalde lengte heeft
-
Geen verzoek verzenden bij een lege string
Dit kan worden uitgebreid met soortgelijke logica.
<input type="text"
name="q"
hx-get="/search/"
hx-trigger="keyup[value.length > 1] delay:400ms"
hx-target="#result">
Hiermee kun je ervoor zorgen dat een verzoek alleen wordt verzonden als de zoekterm langer is dan twee tekens.
load: Uitvoeren zodra pagina of element gereed is
Soms wil je dat bepaalde delen van een pagina met gegevens worden gevuld zodra de pagina wordt geopend. Denk bijvoorbeeld aan dashboardstatistieken, aanbevolen lijsten of notificatiegebieden.
Hiervoor kun je load gebruiken.
<div hx-get="/dashboard/summary/"
hx-trigger="load"
hx-target="this">
Samenvattende informatie wordt geladen...
</div>
Deze code verzendt direct een verzoek zodra het element is geladen en vervangt of werkt zichzelf bij met het antwoord.
Je kunt dit ook gebruiken om niet de hele pagina door de server te laten renderen, maar alleen relatief zware delen later te laden. Dit past goed bij een eenvoudig lazy loading patroon.
revealed: Uitvoeren wanneer zichtbaar op het scherm
Deze trigger heeft een zeer intuïtieve naam. Het verzendt een verzoek wanneer een element op het scherm zichtbaar wordt.
<div hx-get="/posts/next-page/"
hx-trigger="revealed"
hx-swap="afterend">
Meer artikelen worden geladen...
</div>
Deze methode wordt vaak gebruikt voor het implementeren van oneindig scrollen.
Wanneer de gebruiker naar beneden scrollt en het element zichtbaar wordt, wordt de volgende set gegevens geladen en eraan toegevoegd. Dit is zeer aantrekkelijk omdat je hiermee een vrij natuurlijke oneindige scroll kunt implementeren zonder direct de Intersection Observer met JavaScript te hoeven manipuleren.
Houd er echter rekening mee dat revealed eenvoudig en handig is, maar mogelijk tekortschiet wanneer zeer gedetailleerde controle vereist is.
In dat geval past de volgende intersect beter.
intersect: Nauwkeuriger omgaan met de kruising met de viewport
Als revealed meer aanvoelt als "is het zichtbaar?", dan richt intersect zich op het nauwkeuriger omgaan met hoeveel en op welk moment een element kruist met de viewport.
<div hx-get="/analytics/block/"
hx-trigger="intersect once"
hx-target="this">
Analytisch gebied wordt geladen...
</div>
In dit voorbeeld wordt een verzoek slechts één keer verzonden op het moment dat het element kruist met de viewport.
Deze methode is geschikt voor de volgende gevallen:
-
Zware secties later laden op lange pagina's
-
Registreren van het moment van advertentie-/bannerweergave
-
Gegevens alleen laden wanneer een specifiek gebied daadwerkelijk zichtbaar wordt
-
Inhoud stapsgewijs vullen op basis van scrollen
Dit is een functie die je zeker een keer zult gebruiken bij oneindig scrollen, lazy loading en schermen die prestatie-optimalisatie vereisen.
Communicatie tussen server en client: De HX-Trigger header
Naarmate je HTMX langer gebruikt, zul je merken dat het versturen van verzoeken door de browser alleen niet voldoende is. Er komt een moment dat je wilt dat ook andere UI-elementen reageren nadat de serverreactie is voltooid.
Denk bijvoorbeeld aan de volgende situaties:
-
Ik wil de lijst opnieuw laden nadat het opslaan is voltooid
-
Ik wil een succesbericht voor het opslaan weergeven
-
Ik wil ook de teller bijwerken
Je zou dit allemaal kunnen bundelen in client-side JavaScript, maar met HTMX kan de server gebeurtenissen activeren via headers.
Bijvoorbeeld in een Django view:
from django.http import HttpResponse
import json
def save_item(request):
response = HttpResponse("<div>저장 완료</div>")
response["HX-Trigger"] = json.dumps({
"itemSaved": {
"message": "저장이 완료되었습니다."
}
})
return response
De client kan deze gebeurtenis dan gebruiken:
<div hx-get="/items/list/"
hx-trigger="itemSaved from:body"
hx-target="#item-list">
</div>
<div hx-get="/toast/success/"
hx-trigger="itemSaved from:body"
hx-target="#toast-area">
</div>
De voordelen van deze structuur zijn duidelijk.
De server die het opslagverzoek heeft afgehandeld, stuurt niet alleen "Opslaan voltooid HTML" terug, maar kan ook signalen sturen voor vervolgreacties, zoals "nu de lijst bijwerken" of "toon de melding".
Dit betekent dat server en client communiceren via een lossere, gebeurtenisgestuurde structuur, voorbij een eenvoudige verzoek-antwoordrelatie.
Wat klein begint, wordt steeds krachtiger naarmate de UI groeit.
Conclusie
In dit artikel hebben we de kerncontroleattributen van HTMX besproken, met een focus op geavanceerde functies rond hx-trigger.
Samenvattend:
-
hx-triggerbepaalt wanneer een verzoek plaatsvindt -
Het trigger-attribuut omvat standaard DOM-gebeurtenissen van de browser en HTMX-specifieke gebeurtenissen.
-
Met voorwaardelijke triggers kun je verzoeken alleen onder specifieke omstandigheden activeren
-
Met modifier-attributen kun je gebeurtenissen nauwkeurig afstemmen.
-
De
HX-Trigger-header stelt de server in staat om vervolgacties van de client te activeren
Dit vat dit artikel goed samen.
Het is dankbaar dat dit alles mogelijk is geworden zonder één regel JavaScript. Om precies te zijn, "zonder één regel JavaScript die ik zelf schrijf" is de correcte uitdrukking, aangezien de JavaScript-code die via CDN wordt geladen, al in de browser draait.
Gerelateerde artikelen:
- Django en HTMX: Dynamische webontwikkeling vereenvoudigen (Deel 1)
- Django en HTMX: Dynamische webontwikkeling vereenvoudigen - Ajax (Deel 2)
- Django en HTMX: Dynamische webontwikkeling vereenvoudigen (Deel 3): Django-integratie
- Django en HTMX: Dynamische webontwikkeling vereenvoudigen (Deel 4): Payload-overdracht
- Django en HTMX: Dynamische webontwikkeling vereenvoudigen: Formulieren en Serializers gebruiken